Skip to content

Commit

Permalink
Bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
urelja committed Aug 14, 2023
1 parent 312ce89 commit 1dd3a9d
Show file tree
Hide file tree
Showing 13 changed files with 80 additions and 36 deletions.
7 changes: 7 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"python.testing.pytestArgs": [
"tests"
],
"python.testing.unittestEnabled": false,
"python.testing.pytestEnabled": true
}
65 changes: 51 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# deklinacija
A Python library for grammatically correct declension (Serbian: deklinacija) of personal first and last names in Serbian, with support for both Cyrillic and Latin scripts, and even creation of posessive forms, with also a few useful tools for working with the Serbian language. Check out this web demo: https://deklinacija.pythonanywhere.com/
A Python library for grammatically correct declension (Serbian: deklinacija) of personal first and last names in Serbian, with support for both Cyrillic and Latin scripts, and even creation of possessive forms, with also a few useful tools for working with the Serbian language.

## Web Demo
You can easily try out this library's features on this web demo: https://deklinacija.pythonanywhere.com/

## Installation
The source code is currently hosted on GitHub: [https://github.com/urelja/deklinacija](https://github.com/urelja/deklinacija)

The latest binary versions are hosted on the Python Package Index (PyPI): [https://pypi.org/project/deklinacija/](https://pypi.org/project/deklinacija/)
The latest binary versions are hosted on the Python Package Index (PyPI): [https://pypi.org/project/deklinacija/](https://pypi.org/project/deklinacija)
```properties
pip install deklinacija
```
Expand All @@ -23,10 +26,11 @@ import deklinacija as dek
from deklinacija import Gender, Number

genitiv = dek.genitiv("Velja",Gender.MALE) #Velje
dativ = dek.dativ("Petar Petrović",Gender.MALE) #Petru Petroviću
vokativ = dek.vokativ("Predrag",Gender.MALE) #Predraže
vokativ2 = dek.vokativ("PREDRAG JANKOVIĆ",Gender.MALE) #PREDRAŽE JANKOVIĆU
vokativ2 = dek.vokativ("STEFAN JANKOVIĆ",Gender.MALE) #STEFANE JANKOVIĆU
instrumental = dek.instrumental("Uroš",Gender.MALE) #Urošem
lokativ = dek.lokativ("Lana Petrović",Gender.FEMALE) #Lani Petrović
lokativ = dek.lokativ("Beograd",Gender.FEMALE) #Beogradu

print(f"Zdravo, {vokativ}! Dobio si zahtev za prijateljstvo od {genitiv}.")
#Zdravo Predraže! Dobio si zahtev za prijateljstvo od Velje. // Translation: Hello Predrag! You have received a friend request from Velja.
Expand All @@ -48,36 +52,69 @@ print("Dali ste poklon",name['dativ'])
#Dali ste poklon Nikoli // Translation: You have given a gift to Nikola
```

## Posessive Forms
As of version 1.6 there is a new feature: posessive forms. Aside from the name and the gender of the person, you also have to specify the gender of the posessed object and its grammatical number. In case the gender of the posessed object is unknown, you can just pass the object itself to the `object_gender` parameter and the program will figure out which gender it is, provided that the `grammatical_number` is set correctly (default value is Number.SINGULAR).
## Possessive Forms
As of version 1.6 you can now create possessive forms of a name. Aside from the name and the gender of the person, you also have to specify the gender of the possessed object and its grammatical number (singular/plural). In case the gender of the possessed object is unknown, you can just pass the object itself to the `object_gender` parameter and the program will figure out which gender it is, provided that the `grammatical_number` is set correctly (default value is Number.SINGULAR).

The functions in this example return a `string`.
```python
import deklinacija as dek
from deklinacija import Gender, Number

name = dek.posessive(name = "Stefan", gender = Gender.MALE, object_gender = Gender.FEMALE, grammatical_number = Number.SINGULAR)
name2 = dek.posessive(name = "Stefan", gender = Gender.MALE, object_gender = "grupa") #passing the object "group" instead of Gender.FEMALE, default grammatical_number value is Number.SINGULAR so it's not required to specify it in this case
name3 = dek.posessive(name = "Stefan", gender = Gender.MALE, object_gender = "slušalice", grammatical_number = Number.PLURAL) #passing the object "headphones"
name = dek.possessive(name = "Stefan", gender = Gender.MALE, object_gender = Gender.FEMALE, grammatical_number = Number.SINGULAR)
name2 = dek.possessive(name = "Stefan", gender = Gender.MALE, object_gender = "grupa") #passing the object "group" instead of Gender.FEMALE, default grammatical_number value is Number.SINGULAR so it's not required to specify it in this case
name3 = dek.possessive(name = "Stefan", gender = Gender.MALE, object_gender = "slušalice", grammatical_number = Number.PLURAL) #passing the object "headphones"

print(name,"grupa") #Stefanova grupa // Translation: Stefan's group
print(name2,"grupa") #Stefanova grupa
print(name3,"slušalice") #Stefanove slušalice // Translation: Stefan's headphones
```

You can also immediately transform the name through all possible posessive forms (male, female, neutral in singular and plural) by calling the `posessiveAll()` function. Only the `name` and `gender` parameters are required.
You can also immediately transform the name through all possible possessive forms (male, female, neutral in singular and plural) by calling the `possessiveAll()` function. Only the `name` and `gender` parameters are required.

The `posessiveAll()` function returns a `dictionary` where the keys are in the "GENDER_NUMBER" format.
The `possessiveAll()` function returns a `dictionary` where the keys are in the "GENDER_NUMBER" format.

```python
import deklinacija as dek
from deklinacija import Gender, Number

name = dek.posessiveAll("Stefan",Gender.MALE) #{'name': 'Stefan', 'male_singular': 'Stefanov', 'male_plural': 'Stefanovi', 'female_singular': 'Stefanova', 'female_plural': 'Stefanove', 'neutral_singular': 'Stefanovo', 'neutral_plural': 'Stefanova'}
name = dek.possessiveAll("Stefan",Gender.MALE) #{'name': 'Stefan', 'male_singular': 'Stefanov', 'male_plural': 'Stefanovi', 'female_singular': 'Stefanova', 'female_plural': 'Stefanove', 'neutral_singular': 'Stefanovo', 'neutral_plural': 'Stefanova'}

print(name['male_plural'],"prijatelji") #Stefanovi prijatelji // Translation: Stefan's friends
```
## Other tools

## Implementing Into Your Application
In case you are not using Python in your backend and would like to utilise this library, the correct approach would be to create an API which you could call using any programming language you wish (like JavaScript) with a HTTP request. Here's an example of a simple HTTP JSON API built with [Flask](https://flask.palletsprojects.com/en/) that returns a JSON response:
```python
#To install Flask: pip install flask

from flask import Flask
import deklinacija as dek
from deklinacija import Gender, Number

app = Flask(__name__)

@app.route('/api/<name>/<gender>') #for declension
def api(name,gender):
if gender == "male":
gender = Gender.MALE
elif gender == "female":
gender = Gender.FEMALE
result = dek.declineAll(name,gender)
return result #returning a dictionary in Flask returns a JSON response

@app.route('/api/possessive/<name>/<gender>') #for possessive forms
def api2(name,gender):
if gender == "male":
gender = Gender.MALE
elif gender == "female":
gender = Gender.FEMALE
result = dek.possessiveAll(name,gender)
return result

app.run()
```

## Other Tools
This library makes extensive use of a few internal utilities that one may find useful when working with the Serbian language. These include:

`toLatin(word)`, `toCyrillic(word)`: Converts input string from Latin into Cyrillic and vice versa. Works with Serbian Latin letter pairs (lj, nj, dž, etc.)
Expand All @@ -87,7 +124,7 @@ This library makes extensive use of a few internal utilities that one may find u
`isLatin(word)`: Returns a boolean indicating whether the last character of the input string is in the Latin script. Raises an error if the character isn't in neither Latin or Cyrillic.

## Todo
All promised features have been implemented. All that is left is code cleanup. Feel free to suggest new features on the GitHub page.
All promised features have been implemented. Feel free to suggest new features on the GitHub page.


## Attribution
Expand Down
2 changes: 1 addition & 1 deletion deklinacija/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
from deklinacija.main import genitiv, dativ, akuzativ, vokativ, instrumental, lokativ, declineAll, possesive, posessiveAll
from deklinacija.main import genitiv, dativ, akuzativ, vokativ, instrumental, lokativ, declineAll, possessive, possessiveAll
from deklinacija.utils import toCyrillic, toLatin, separateLetters, isLatin, Number, Gender
26 changes: 13 additions & 13 deletions deklinacija/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,19 +183,19 @@ def lokativ(name, gender):

return " ".join(returnName)

def possesive(name,gender,object_gender,grammatical_number=Number.SINGULAR):
def possessive(name,gender,object_gender,grammatical_number=Number.SINGULAR):
"""
ČIJI? WHOSE?
Returns the possesive form of the provided name. Depends on the object_gender and grammatical_number parameters to add the appropriate suffix to the name.
Returns the possessive form of the provided name. Depends on the object_gender and grammatical_number parameters to add the appropriate suffix to the name.
Parameters:
name: The name of the person that posseses something
gender: The gender of the person that posesses something, param value must be either Gender.MALE or Gender.FEMALE
object_gender: Can either be the gender of the object that the person posesses (value must be Gender.MALE, Gender.FEMALE or Gender.NEUTRAL) or the object itself, in which case the gender will be automatically detected provided that the grammatical_number param is correct
grammatical_number: The grammatical number of the object that the person posesses. Param value must be either Number.SINGULAR or Number.PLURAL. Default: singular
gender: The gender of the person that possesses something, param value must be either Gender.MALE or Gender.FEMALE
object_gender: Can either be the gender of the object that the person possesses (value must be Gender.MALE, Gender.FEMALE or Gender.NEUTRAL) or the object itself, in which case the gender will be automatically detected provided that the grammatical_number param is correct
grammatical_number: The grammatical number of the object that the person possesses. Param value must be either Number.SINGULAR or Number.PLURAL. Default: singular
"""
utils.checkPosessive(name,gender,object_gender,grammatical_number,)
utils.checkPossessive(name,gender,object_gender,grammatical_number,)


if type(object_gender) == str:
Expand Down Expand Up @@ -238,11 +238,11 @@ def possesive(name,gender,object_gender,grammatical_number=Number.SINGULAR):
suffix = utils.toLatin(POSSESIVE_SUFFIXES[gender_text+"_"+number_text])
else:
suffix = POSSESIVE_SUFFIXES[gender_text+"_"+number_text]
return __possesive(name,gender)+suffix
return __possessive(name,gender)+suffix

def posessiveAll(name,gender):
def possessiveAll(name,gender):
"""
Creates all possible posessive forms (male, female and neutral in plural and singular) of the provided name and returns a dictionary where the keys are in the "GENDER_NUMBER" format.
Creates all possible possessive forms (male, female and neutral in plural and singular) of the provided name and returns a dictionary where the keys are in the "GENDER_NUMBER" format.
Parameters:
name: The name that should be transformed
Expand All @@ -251,9 +251,9 @@ def posessiveAll(name,gender):
utils.check(name, gender)
name = name.strip()

return {"name":name,"male_singular":possesive(name,gender,Gender.MALE,Number.SINGULAR),"male_plural":possesive(name,gender,Gender.MALE,Number.PLURAL),"female_singular":possesive(name,gender,Gender.FEMALE,Number.SINGULAR),"female_plural":possesive(name,gender,Gender.FEMALE,Number.PLURAL),"neutral_singular":possesive(name,gender,Gender.NEUTRAL,Number.SINGULAR),"neutral_plural":possesive(name,gender,Gender.NEUTRAL,Number.PLURAL)}
return {"name":name,"male_singular":possessive(name,gender,Gender.MALE,Number.SINGULAR),"male_plural":possessive(name,gender,Gender.MALE,Number.PLURAL),"female_singular":possessive(name,gender,Gender.FEMALE,Number.SINGULAR),"female_plural":possessive(name,gender,Gender.FEMALE,Number.PLURAL),"neutral_singular":possessive(name,gender,Gender.NEUTRAL,Number.SINGULAR),"neutral_plural":possessive(name,gender,Gender.NEUTRAL,Number.PLURAL)}

def __possesive(name,gender):
def __possessive(name,gender):
utils.check(name, gender)
name = utils.separateLetters(__genitiv(name.strip(),gender))
lastChar = name[-1]
Expand Down Expand Up @@ -382,7 +382,7 @@ def __possesive(name,gender):
return "".join(name)

def __genitiv(name, gender):
#utils.check(name, gender)
utils.check(name, gender)
name = utils.separateLetters(name.strip())

lastChar = name[-1]
Expand Down Expand Up @@ -579,7 +579,7 @@ def declineAll(name, gender):
name: The name that should be declined
gender: The gender of the person, param value must be either Gender.MALE or Gender.FEMALE
"""
#utils.check(name, gender)
utils.check(name, gender)
name = name.strip()

allDek = {"nominativ": name, "genitiv": genitiv(name, gender), "dativ": dativ(name, gender), "akuzativ": akuzativ(
Expand Down
2 changes: 1 addition & 1 deletion deklinacija/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def check(name, gender):
raise ValueError("name param must be at least 3 characters long")


def checkPosessive(name,gender,object_gender,grammatical_number):
def checkPossessive(name,gender,object_gender,grammatical_number):
if type(name) != str:
raise TypeError(
"name param must be string")
Expand Down
Binary file removed dist/deklinacija-1.7.0-py3-none-any.whl
Binary file not shown.
Binary file removed dist/deklinacija-1.7.0.tar.gz
Binary file not shown.
Binary file added dist/deklinacija-1.7.1-py3-none-any.whl
Binary file not shown.
Binary file added dist/deklinacija-1.7.1.tar.gz
Binary file not shown.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "hatchling.build"

[project]
name = "deklinacija"
version = "1.7.0"
version = "1.7.1"
authors = [
{ name="urelja", email="code.relja@gmail.com" },
]
Expand Down
File renamed without changes.
File renamed without changes.
12 changes: 6 additions & 6 deletions tests/test_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
module_directory = os.path.dirname(module_path)
file_path_male = os.path.join(module_directory, 'names_male.txt')
file_path_female = os.path.join(module_directory, 'names_female.txt')
file_path_male_p = os.path.join(module_directory, 'names_male_posessive.txt')
file_path_female_p = os.path.join(module_directory, 'names_female_posessive.txt')
file_path_male_p = os.path.join(module_directory, 'names_male_possessive.txt')
file_path_female_p = os.path.join(module_directory, 'names_female_possessive.txt')

with open(file_path_male, "r", encoding="utf-8") as fileM:
read_content_male = fileM.readlines()
Expand Down Expand Up @@ -43,20 +43,20 @@ def testFemaleNames():
assert compare == nameDict
n += 1

def testPosessiveMaleNames():
def testPossessiveMaleNames():
n = 0
for i in read_content_male_p:
nameDict = ast.literal_eval(read_content_male_p[n].strip())
name = nameDict['name']
compare = dek.posessiveAll(name,dek.Gender.MALE)
compare = dek.possessiveAll(name,dek.Gender.MALE)
assert compare == nameDict
n += 1

def testPosessiveFemaleNames():
def testPossessiveFemaleNames():
n = 0
for i in read_content_female_p:
nameDict = ast.literal_eval(read_content_female_p[n].strip())
name = nameDict['name']
compare = dek.posessiveAll(name,dek.Gender.FEMALE)
compare = dek.possessiveAll(name,dek.Gender.FEMALE)
assert compare == nameDict
n += 1

0 comments on commit 1dd3a9d

Please sign in to comment.