From 5e801208833e813ef5c416b56ad4b2f9e0edb2ef Mon Sep 17 00:00:00 2001 From: Sibylle Date: Tue, 2 May 2023 11:37:12 +0200 Subject: [PATCH 01/46] update README --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c4f8bc5..a5ea4ed 100644 --- a/README.md +++ b/README.md @@ -41,3 +41,5 @@ python script.py --help ``` If you run this script, you become proud of yourself. + +# First, I'll try to make a few changes though \ No newline at end of file From ba16fa4ac1d2c5d05b4d3331c11e6085484d064b Mon Sep 17 00:00:00 2001 From: Sibylle Date: Tue, 2 May 2023 12:05:03 +0200 Subject: [PATCH 02/46] update tutorial --- tutorial.md | 1 + 1 file changed, 1 insertion(+) diff --git a/tutorial.md b/tutorial.md index 6add32e..24a2674 100644 --- a/tutorial.md +++ b/tutorial.md @@ -1,3 +1,4 @@ # How to use my script In this notebook you will learn how to use the functions and classes defined in `script.py`. +This will become a tutorial \ No newline at end of file From 793b45a7872e9143d9d46e09662ec5369b746913 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Tue, 2 May 2023 12:21:03 +0200 Subject: [PATCH 03/46] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a5ea4ed..0d1200e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# A script to make you proud +# A script to make you proud -- really! This repository contains a small Python program that shows that I have learned Python in this semester. @@ -42,4 +42,4 @@ python script.py --help If you run this script, you become proud of yourself. -# First, I'll try to make a few changes though \ No newline at end of file +# First, I'll try to make a few changes though From 6da366e14d420339c3db27c5f332f4cd49cdcc00 Mon Sep 17 00:00:00 2001 From: Sibylle Date: Mon, 8 May 2023 17:23:06 +0200 Subject: [PATCH 04/46] update README --- README.md | 58 +++++++++++++++++-------------------------------------- 1 file changed, 18 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 0d1200e..2d8ac6d 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,23 @@ -# A script to make you proud -- really! +# What I'm planning -This repository contains a small Python program that shows that I have learned Python in this semester. +- Start from the translation job management script created for the first assignment, but expand on it to make it useful for a translation agency rather than an independent translator. +- Three class attributes (strings) : + - Translator + - Revisor + - Status + -> The default value for "Translator" and "Revisor" is "Internal", meaning that an employee of the translation agency took up the job. If the agency assigned the job to a freelancer, the default value can be changed to their name. + -> The default value for "Status" is "Created". The status can then be updated as the project progresses to "In translation", "In revision", "Delivered", "Delayed" or "Cancelled". If possible, the script should only accept these six labels to prevent organisational chaos due to everyone using their own labels. +- Instance and computed attributes remain the same as in the first assignment (with some edits to add the advice from the first assignment's feedback). +- Add validation for unexpected input (+ for "Status" labels different from the six authorised labels?) +- Add methods (?) to call the computed attributes and get a result that's more legible than what this currently generates (for example "22 days" instead of "datetime.timedelta(days=22)") +- The input will still be read from a list of dictionaries in a separate json-file. Those dictionaries will be described in a separate markdown file. -The code has been developed by Mariana Montes. +# What I'd like to add but don't know how -## Installation and usage +- It would be neat if I could do something with the translation memory and termbase of each project. Maybe add a method that opens them for a preview? +- It would also be super useful to have a way to align a source and a target text and generate a file that can be added to a translation memory. So, to start from two docx-files (or txt-files), split them into sentences and pair each sentence in the source text with the corresponding sentence in the target text and generate a single xml-file with the paired sentences. Ideally, the context (i.e. the surrounding sentences) should also be considered, but if that's not possible an aligned xml would already be awesome. -Clone this repository with the following git command in the console (or download the ZIP file via GitHub): +# Things I'm not yet sure how to integrate into the assignment -```sh -git clone git@github.com:montesmariana/intro_machine_learning_using_python -``` - -You can import the script as a module by adding the repository to your path in a Python script or interactive notebook and then calling `import`. - -```python -import sys -sys.path.append('/path/to/intro_machine_learning_using_python') -import script as s -``` - -Check out `tutorial.md` to see an example of the different functionalities of the script! - -You can also run the script directly with the following code in a console: - -```sh -python script.py -``` - -Or in Jupyter notebook with: - -```python -%run script.py -``` - -In both cases `example.json` stands for the `filename` argument that the script needs. You can use [the file in this repository](example.json) or a similar file of yours. Find more information on how this script works with: - -```sh -python script.py --help -``` - -If you run this script, you become proud of yourself. - -# First, I'll try to make a few changes though +- Use of argparse. +- Use of regular expressions. \ No newline at end of file From 47df9095ad45284d9e4d730c44139aafb76e0616 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Mon, 8 May 2023 17:29:45 +0200 Subject: [PATCH 05/46] Update README.md --- README.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 2d8ac6d..a26231d 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,10 @@ - Translator - Revisor - Status - -> The default value for "Translator" and "Revisor" is "Internal", meaning that an employee of the translation agency took up the job. If the agency assigned the job to a freelancer, the default value can be changed to their name. - -> The default value for "Status" is "Created". The status can then be updated as the project progresses to "In translation", "In revision", "Delivered", "Delayed" or "Cancelled". If possible, the script should only accept these six labels to prevent organisational chaos due to everyone using their own labels. + + -> The default value for "Translator" and "Revisor" is "Internal", meaning that an employee of the translation agency took up the job. If the agency assigned the job to a freelancer, the default value can be changed to their name. + + -> The default value for "Status" is "Created". The status can then be updated as the project progresses to "In translation", "In revision", "Delivered", "Delayed" or "Cancelled". If possible, the script should only accept these six labels to prevent organisational chaos due to everyone using their own labels. - Instance and computed attributes remain the same as in the first assignment (with some edits to add the advice from the first assignment's feedback). - Add validation for unexpected input (+ for "Status" labels different from the six authorised labels?) - Add methods (?) to call the computed attributes and get a result that's more legible than what this currently generates (for example "22 days" instead of "datetime.timedelta(days=22)") @@ -20,4 +22,4 @@ # Things I'm not yet sure how to integrate into the assignment - Use of argparse. -- Use of regular expressions. \ No newline at end of file +- Use of regular expressions. From 12a3cad4f1291169cae1a4699be167e27ec1c109 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Mon, 8 May 2023 18:02:39 +0200 Subject: [PATCH 06/46] Add files via upload For reference, these are examples of translation memory (TM) and termbase (TB) formats compatible with most CAT-tools (computer-assisted translation tools). --- Translation-technology_TBexample.csv | 24 + Translation-technology_TBexample.xdl | Bin 0 -> 358488 bytes Translation-technology_TBexample.xdt | 1217 ++++++++++++++++++++++++++ Translation-technology_TBexample.xml | Bin 0 -> 72188 bytes Translation-technology_TMexample.tmx | Bin 0 -> 49376 bytes 5 files changed, 1241 insertions(+) create mode 100644 Translation-technology_TBexample.csv create mode 100644 Translation-technology_TBexample.xdl create mode 100644 Translation-technology_TBexample.xdt create mode 100644 Translation-technology_TBexample.xml create mode 100644 Translation-technology_TMexample.tmx diff --git a/Translation-technology_TBexample.csv b/Translation-technology_TBexample.csv new file mode 100644 index 0000000..5568dcf --- /dev/null +++ b/Translation-technology_TBexample.csv @@ -0,0 +1,24 @@ +Entry_ID,Entry_Subject,Entry_Domain,Entry_ClientID,Entry_ProjectID,Entry_Created,Entry_Creator,English,French +0,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:40:45 PM,Sibylle,machine translation,traduction automatique +1,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:43:07 PM,Sibylle,advanced topics,sujets avancés +2,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:43:42 PM,Sibylle,translator,traducteur +3,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:43:58 PM,Sibylle,localiser,localisateur +4,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:44:10 PM,Sibylle,revisor,réviseur +5,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:44:27 PM,Sibylle,website,site web +6,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:44:43 PM,Sibylle,software,software +7,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:44:53 PM,Sibylle,game,jeu +8,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:45:02 PM,Sibylle,subtitler,sous-titreur +9,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:45:12 PM,Sibylle,post-editor,post-éditeur +10,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:45:40 PM,Sibylle,technical writer,rédacteur technique +11,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:46:02 PM,Sibylle,computational linguist,linguiste informatique +12,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:46:15 PM,Sibylle,translation technology,technologies de la traduction +13,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:46:34 PM,Sibylle,work placement,stage +14,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:48:18 PM,Sibylle,data,données +15,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:50:06 PM,Sibylle,statistical,statistique +16,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:50:31 PM,Sibylle,neural,neuronal +17,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:50:41 PM,Sibylle,hybrid,hybride +18,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:50:51 PM,Sibylle,adaptive,adaptatif +19,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:53:07 PM,Sibylle,MT,traduction automatique +20,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:53:31 PM,Sibylle,MT engine,engin de traduction automatique +21,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:54:20 PM,Sibylle,post-editing,post-édition +22,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:55:51 PM,Sibylle,pre-editing,pré-édition diff --git a/Translation-technology_TBexample.xdl b/Translation-technology_TBexample.xdl new file mode 100644 index 0000000000000000000000000000000000000000..0eff8c015a83061d994b44fe5d7c960766f707e7 GIT binary patch literal 358488 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 b(@?AYiaOECBkIpS^$5EvIA+k1SZe%#J~lV= literal 0 HcmV?d00001 diff --git a/Translation-technology_TBexample.xdt b/Translation-technology_TBexample.xdt new file mode 100644 index 0000000..c3746b7 --- /dev/null +++ b/Translation-technology_TBexample.xdt @@ -0,0 +1,1217 @@ + + + + + + Entry level + 0 + 1 + 1 + 0 + + + + Index level + 2 + 1 + 1 + 0 + + + + Term level + 3 + 1 + 1 + 0 + + + + Usage example + 1 + 0 + 0 + 0 + + + + + Part of speech + 1 + 0 + 0 + 0 + + + + + Number (grammar) + 1 + 0 + 0 + 0 + + + + + Gender (grammar) + 1 + 0 + 0 + 0 + + + + + Definition + 1 + 0 + 0 + 0 + + + + + Status + 1 + 0 + 0 + 1 + + + + + + Definition + 1 + 0 + 0 + 0 + + + + + + Client + 1 + 0 + 0 + 0 + + + + + Domain + 1 + 0 + 0 + 0 + + + + + ID + 1 + 0 + 0 + 0 + + + + + Note + 1 + 0 + 0 + 0 + + + + + Project + 1 + 0 + 0 + 0 + + + + + Subject + 1 + 0 + 0 + 0 + + + + + Image + 1 + 0 + 0 + 0 + + + + + + + NL + Dutch + EN + English + FR + French + + + + ID + + Number + F + + + + Note + + Text + F + + + + Project + + Text + F + + + + Client + + Text + F + + + + Domain + + Text + F + + + + Subject + + Text + F + + + + Definition + + Text + F + + + + Usage example + + Text + F + + + + Status + + Picklist + F + Forbidden + None + + + + Part of speech + + Picklist + F + N + Adj + Adv + V + X + + + + Number (grammar) + + Picklist + F + Sing + Pl + + + + Gender (grammar) + + Picklist + F + Masc + Fem + N + + + + Image + + Multimedia File + F + + + + + 0 + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Translation-technology_TBexample.xml b/Translation-technology_TBexample.xml new file mode 100644 index 0000000000000000000000000000000000000000..9daff3d9984db63f82938fe013737554805ea7a6 GIT binary patch literal 72188 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 literal 0 HcmV?d00001 diff --git a/Translation-technology_TMexample.tmx b/Translation-technology_TMexample.tmx new file mode 100644 index 0000000000000000000000000000000000000000..d3266338364288b77210b0f73b5cc416d1843db4 GIT binary patch literal 49376 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 NouS(0=>0Xa_y45EI`04g literal 0 HcmV?d00001 From 229d6db2a8597179716521a34d0752ce00358488 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Tue, 9 May 2023 10:50:08 +0200 Subject: [PATCH 07/46] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a26231d..e49b05e 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ # What I'd like to add but don't know how - It would be neat if I could do something with the translation memory and termbase of each project. Maybe add a method that opens them for a preview? -- It would also be super useful to have a way to align a source and a target text and generate a file that can be added to a translation memory. So, to start from two docx-files (or txt-files), split them into sentences and pair each sentence in the source text with the corresponding sentence in the target text and generate a single xml-file with the paired sentences. Ideally, the context (i.e. the surrounding sentences) should also be considered, but if that's not possible an aligned xml would already be awesome. +- It would also be super useful to have a way to align a source and a target text and generate a file that can be added to a translation memory. So, to start from two docx-files (or txt-files), split them into sentences and pair each sentence in the source text with the corresponding sentence in the target text and generate a single csv-file with the paired sentences. Ideally, the context (i.e. the surrounding sentences) should also be considered, but if that's not possible an aligned xml would already be awesome. # Things I'm not yet sure how to integrate into the assignment From d47347183e0b4907ad8cee0f792408898ba0290a Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Tue, 9 May 2023 10:57:46 +0200 Subject: [PATCH 08/46] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e49b05e..c7b1b96 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ # What I'd like to add but don't know how - It would be neat if I could do something with the translation memory and termbase of each project. Maybe add a method that opens them for a preview? -- It would also be super useful to have a way to align a source and a target text and generate a file that can be added to a translation memory. So, to start from two docx-files (or txt-files), split them into sentences and pair each sentence in the source text with the corresponding sentence in the target text and generate a single csv-file with the paired sentences. Ideally, the context (i.e. the surrounding sentences) should also be considered, but if that's not possible an aligned xml would already be awesome. +- It would also be super useful to have a way to align a source and a target text and generate a file that can be added to a translation memory. So, to start from two docx-files (or txt-files), split them into sentences and pair each sentence in the source text with the corresponding sentence in the target text and generate a single csv-file with the paired sentences. Ideally, the context (i.e. the surrounding sentences) should also be considered, but if that's not possible an aligned csv would already be awesome. # Things I'm not yet sure how to integrate into the assignment From 3612e1126805c425c0f9d346e2e3f65f8f2940ee Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Fri, 19 May 2023 15:53:31 +0200 Subject: [PATCH 09/46] Add files via upload --- Final-assignment_trial-and-error.ipynb | 259 +++++++++++++++++++++++++ 1 file changed, 259 insertions(+) create mode 100644 Final-assignment_trial-and-error.ipynb diff --git a/Final-assignment_trial-and-error.ipynb b/Final-assignment_trial-and-error.ipynb new file mode 100644 index 0000000..5d24822 --- /dev/null +++ b/Final-assignment_trial-and-error.ipynb @@ -0,0 +1,259 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "cf98a968", + "metadata": {}, + "outputs": [], + "source": [ + "import datetime #datetime package to convert strings into dates, calculate time periods etc." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "ffae2adb", + "metadata": {}, + "outputs": [], + "source": [ + "class Translation:\n", + " translator = \"Internal\" # class attribute\n", + " revisor = \"Internal\"\n", + " status = \"created\"\n", + "\n", + " def _is_valid_status(self, status):\n", + " if not status.lower() in [\"created\",\n", + " \"in translation\",\n", + " \"in revision\",\n", + " \"delivered\",\n", + " \"delayed\",\n", + " \"cancelled\",\n", + " \"canceled\"]:\n", + " return \"Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.\"\n", + "\n", + " def __init__(self, title, client, source, target, words, start, deadline, price, tm, domain = ''):\n", + " # 'self' represents the object (= class element) itself\n", + " self.title = title\n", + " self.client = client\n", + " self.source = source\n", + " self.target = target\n", + " self.words = words\n", + " self.start = start\n", + " self.deadline = deadline\n", + " self.price = price\n", + " self.tm = tm\n", + " self.domain = domain\n", + " \n", + " today = datetime.date.today() # current date\n", + " self.st = datetime.date.fromisoformat(start) # turns string into date\n", + " self.dl = datetime.date.fromisoformat(deadline) # turns string into date\n", + " self.daysleft = self.dl - today # difference between deadline and current date\n", + " self.length = self.dl - self.st # difference between deadline and start date\n", + " self.rate = self.price/self.words # word rate (price divided by word count)\n", + " self.efficiency = words/self.length.days # words to translate per day (word count divided by project length, see 'length' in explanations above)\n", + "\n", + " def days_left(self):\n", + " # prints a text indicating how many days are left until the project deadline\n", + " if self.dl < datetime.date.today():\n", + " # if the deadline is in the past\n", + " return f\"The deadline has been exceeded already.\"\n", + " else:\n", + " # if the deadline is not in the past\n", + " return f\"There are {self.daysleft.days} days left until the deadline.\"\n", + " \n", + " def length(self):\n", + " return f\"{self.length.days} days\"\n", + " \n", + " def __str__(self):\n", + " # defines the print behaviour: returns a text providing the main information about the project\n", + " sent_1 = f\"{self.title} is a translation for {self.client} from {self.source} into {self.target}.\"\n", + " if self.translator == \"Internal\" and self.revisor == \"Internal\":\n", + " sent_2 = f\"Both the translator and the revisor are agency collaborators.\"\n", + " elif self.translator == \"Internal\" and self.revisor != \"Internal\":\n", + " sent_2 = f\"The translator is an agency collaborator and the revisor is {self.revisor}.\"\n", + " elif self.translator != \"Internal\" and self.revisor == \"Internal\":\n", + " sent_2 = f\"The translator is {self.translator} and the revisor is an agency collaborator.\"\n", + " else:\n", + " sent_2 = f\"The translator is {self.translator} and the revisor is {self.revisor}.\"\n", + " # this if-statement considers whether a domain was added\n", + " if len(self.domain) > 0:\n", + " sent_3 = f\"The domain is: {self.domain}.\"\n", + " else:\n", + " sent_3 = \"The domain is unspecified.\" # if no domain was added, the text mentions it\n", + " sent_4 = f\"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word.\" #the word rate is rounded to two decimal places to avoid cumbersomely long numbers\n", + " # this if-statement considers whether the deadline is in the past\n", + " if self.dl < datetime.date.today():\n", + " sent_5 = f\"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day.\" # the efficiency is rounded to units because you can't translate a fraction of a word anyway\n", + " else:\n", + " sent_5 = f\"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day.\"\n", + " # this if-statement considers whether there is a translation memory for the project\n", + " if self.tm is True:\n", + " sent_6 = f\"There is a translation memory.\"\n", + " else:\n", + " sent_6 = f\"There is no translation memory\"\n", + " sent_7 = f\"The project is currently {self.status}.\"\n", + " # print each sentence in a different line\n", + " return \"\\n\".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7])" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "086c20b8", + "metadata": {}, + "outputs": [], + "source": [ + "test = Translation('Guide de Bruxelles', 'Foodies', 'NL', 'FR', 11500, '2023-03-22', '2023-05-06', 1610, False)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "a342339f", + "metadata": {}, + "outputs": [], + "source": [ + "test.status = \"in translation\"" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "1334e6a5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'ongoing'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test.status" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "f308ff5a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.'" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test._is_valid_status(\"ongoing\")" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "e7262ea0", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "'datetime.timedelta' object is not callable", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[22], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mtest\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlength\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", + "\u001b[0;31mTypeError\u001b[0m: 'datetime.timedelta' object is not callable" + ] + } + ], + "source": [ + "test.length()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "8a4974ea", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", + "Both the translator and the revisor are agency collaborators.\n", + "The domain is unspecified.\n", + "It's 11500 words long, with a rate of 0.14 € per word.\n", + "It started on 2023-03-22 and was due on 2023-05-06, so 45 days were foreseen for it. To meet the deadline, 256 words needed to be translated or revised per day.\n", + "There is no translation memory\n", + "The project is currently in translation.\n" + ] + } + ], + "source": [ + "print(test)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "3f06d6c4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'2023-03-22'" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "test.start" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c9e596c", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From e2ee48c503a99bad0d15bde8891d1761feea181c Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Fri, 19 May 2023 20:57:41 +0200 Subject: [PATCH 10/46] Add files via upload --- translation-memory_1.csv | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 translation-memory_1.csv diff --git a/translation-memory_1.csv b/translation-memory_1.csv new file mode 100644 index 0000000..4ed4e54 --- /dev/null +++ b/translation-memory_1.csv @@ -0,0 +1,3 @@ +Introduction to Machine Learning with Python.,Introduction au machine learning à l’aide de Python. +This module provides an introduction to the basic concepts and use of the Python programming language in support of translation.,Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. +"Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.","L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning." From 7d8cefc0f6f1fc82bf82bea82c76c6a4f286e577 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Fri, 19 May 2023 21:00:42 +0200 Subject: [PATCH 11/46] Add files via upload --- translation-memory_2.csv | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 translation-memory_2.csv diff --git a/translation-memory_2.csv b/translation-memory_2.csv new file mode 100644 index 0000000..ae72263 --- /dev/null +++ b/translation-memory_2.csv @@ -0,0 +1,4 @@ +EN,FR +Introduction to Machine Learning with Python.,Introduction au machine learning à l’aide de Python. +This module provides an introduction to the basic concepts and use of the Python programming language in support of translation.,Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. +"Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.","L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning." From cda27d5498b4391900979c69da632687dc42a43f Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Mon, 22 May 2023 21:28:06 +0200 Subject: [PATCH 12/46] Presentation 23-05 Backup for presentation on 23-05. --- Demo-notebook.ipynb | 802 +++++++++++++++++++++++++++++++ python_en.txt | 1 + python_fr.txt | 1 + translation-memory_1.csv | 1 + translation_agency_projects.json | 1 + translation_projects.json | 1 + 6 files changed, 807 insertions(+) create mode 100644 Demo-notebook.ipynb create mode 100644 python_en.txt create mode 100644 python_fr.txt create mode 100644 translation_agency_projects.json create mode 100644 translation_projects.json diff --git a/Demo-notebook.ipynb b/Demo-notebook.ipynb new file mode 100644 index 0000000..cfac671 --- /dev/null +++ b/Demo-notebook.ipynb @@ -0,0 +1,802 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b9f1fa80", + "metadata": {}, + "source": [ + "# Translation project manager for translation agency" + ] + }, + { + "cell_type": "markdown", + "id": "f0b84575", + "metadata": {}, + "source": [ + "## Starting point: Translation project manager for freelancer\n", + "\n", + "This script consisted of a single class `Translation` with the following attributes:\n", + "- A class attribute `translator`, which defaults to the freelancer's name.\n", + "- 10 attributes provided at initialisation:\n", + " - `title`(a string) indicates the project's title (typically the title of the source document, or the overall title the translator gave the project if there's more than one document to be translated);\n", + " - `client` (a string) indicates the client who ordered the translation;\n", + " - `source` (a string) indicates the language of the source document (document to be translated);\n", + " - `target` (a string) indicates the language of the target document (translation);\n", + " - `words` (an integer) indicates the word count of the source document;\n", + " - `start`(a string) indicates the project's start date in ISO format (YYYY-MM-DD);\n", + " - `deadline`(a string) indicates the project's deadline in ISO format (YYYY-MM-DD);\n", + " - `price` (an integer) indicates the total price invoiced to the client (excl. VAT);\n", + " - `tm` (a boolean) indicates whether or not a translation memory is available for this project;\n", + " - `domain` (a string) indicates the overall domain to which the project belongs.\n", + "- 4 computed attributes:\n", + " - `daysleft`, which calculates the number of days left until the project deadline by subtracting the current date from the deadline;\n", + " - `length`, which calculates the total number of days allotted for the project by subtracting the start date from the deadline;\n", + " - `rate`, which calculates the word rate for the project by dividing the total price by the word count in the source document;\n", + " - `efficiency`, which calculates how many words the translator needs to translate per day to meet the deadline." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b11c57bc", + "metadata": {}, + "outputs": [], + "source": [ + "import datetime #datetime package to convert strings into dates, calculate time periods etc." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "1f6d67d3", + "metadata": {}, + "outputs": [], + "source": [ + "class Translation:\n", + " translator = \"Sibylle\" # class attribute\n", + "\n", + " def __init__(self, title, client, source, target, words, start, deadline, price, tm, domain = ''):\n", + " # 'self' represents the object (= class element) itself\n", + " self.title = title\n", + " self.client = client\n", + " self.source = source\n", + " self.target = target\n", + " self.words = words\n", + " self.start = datetime.date.fromisoformat(start) # turns string into date\n", + " self.deadline = datetime.date.fromisoformat(deadline) # turns string into date\n", + " self.price = price\n", + " self.tm = tm\n", + " self.domain = domain\n", + " \n", + " today = datetime.date.today() # current date\n", + " self.daysleft = self.deadline - today # difference between deadline and current date\n", + " self.length = self.deadline - self.start # difference between deadline and start date\n", + " self.rate = self.price/self.words # word rate (price divided by word count)\n", + " self.efficiency = words/self.length.days # words to translate per day (word count divided by project length, see 'length' in explanations above)\n", + "\n", + " def days_left(self):\n", + " # prints a text indicating how many days are left until the project deadline\n", + " if self.deadline < datetime.date.today():\n", + " # if the deadline is in the past\n", + " return f\"The deadline has been exceeded already.\"\n", + " else:\n", + " # if the deadline is not in the past\n", + " return f\"There are {self.daysleft.days} days left until the deadline.\"\n", + " \n", + " def __str__(self):\n", + " # defines the print behaviour: returns a text providing the main information about the project\n", + " sent_1 = f\"{self.title} is a translation for {self.client} from {self.source} into {self.target}.\"\n", + " # this if-statement considers whether a domain was added\n", + " if len(self.domain) > 0:\n", + " sent_2 = f\"The domain is: {self.domain}.\"\n", + " else:\n", + " sent_2 = \"The domain is unspecified.\" # if no domain was added, the text mentions it\n", + " sent_3 = f\"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word.\" #the word rate is rounded to two decimal places to avoid cumbersomely long numbers\n", + " # this if-statement considers whether the deadline is in the past\n", + " if self.deadline < datetime.date.today():\n", + " sent_4 = f\"It started on {self.start} and was due on {self.deadline}, so I had {self.length.days} days for it. I needed to translate {round(self.efficiency,0)} words per day to meet the deadline.\" # the efficiency is rounded to units because you can't translate a fraction of a word anyway\n", + " else:\n", + " sent_4 = f\"It started on {self.start} and is due on {self.deadline}, so I have {self.length.days} days for it, of which {self.daysleft.days} left. I need to translate {round(self.efficiency,0)} words per day to meet the deadline.\"\n", + " # this if-statement considers whether there is a translation memory for the project\n", + " if self.tm is True:\n", + " sent_5 = f\"There is a translation memory.\"\n", + " else:\n", + " sent_5 = f\"There is no translation memory\"\n", + " # print each sentence in a different line\n", + " return \"\\n\".join([sent_1, sent_2, sent_3, sent_4, sent_5])" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "e92b9a05", + "metadata": {}, + "outputs": [], + "source": [ + "test1 = Translation('Guide de Bruxelles', 'Foodies', 'NL', 'FR', 11500, '2023-03-22', '2023-05-06', 1610, False)" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "ac4d553d", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", + "The domain is unspecified.\n", + "It's 11500 words long, with a rate of 0.14 € per word.\n", + "It started on 2023-03-22 and was due on 2023-05-06, so I had 45 days for it. I needed to translate 256.0 words per day to meet the deadline.\n", + "There is no translation memory\n" + ] + } + ], + "source": [ + "print(test1)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c4ec3c60", + "metadata": {}, + "outputs": [], + "source": [ + "import json" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9517724c", + "metadata": {}, + "outputs": [], + "source": [ + "translations_file = 'translation_projects.json' # assign filename to a string variable\n", + "with open(translations_file, encoding = 'utf-8') as f:\n", + " # open file and use json to parse it\n", + " translations = json.load(f) # translations is now a list of dictionaries. " + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d52e308a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "La polyarthrite rhumatoïde et autres rhumatismes inflammatoires is a translation for Reuma vzw from FR into NL.\n", + "The domain is: healthcare.\n", + "It's 2131 words long, with a rate of 0.1 € per word.\n", + "It started on 2020-09-24 and was due on 2020-10-15, so I had 21 days for it. I needed to translate 101.0 words per day to meet the deadline.\n", + "There is no translation memory\n", + "----\n", + "Handboek voor studentenvertegenwoordigers is a translation for KU Leuven from NL into EN.\n", + "The domain is: education.\n", + "It's 3654 words long, with a rate of 0.15 € per word.\n", + "It started on 2023-02-21 and was due on 2023-03-02, so I had 9 days for it. I needed to translate 406.0 words per day to meet the deadline.\n", + "There is a translation memory.\n", + "----\n", + "User Guide MFPs is a translation for UGent from EN into NL.\n", + "The domain is unspecified.\n", + "It's 1852 words long, with a rate of 0.15 € per word.\n", + "It started on 2023-04-14 and was due on 2023-04-16, so I had 2 days for it. I needed to translate 926.0 words per day to meet the deadline.\n", + "There is a translation memory.\n", + "----\n", + "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", + "The domain is unspecified.\n", + "It's 11500 words long, with a rate of 0.14 € per word.\n", + "It started on 2023-03-22 and was due on 2023-05-06, so I had 45 days for it. I needed to translate 256.0 words per day to meet the deadline.\n", + "There is no translation memory\n", + "----\n" + ] + } + ], + "source": [ + "# go through each of the items in the list\n", + "for translation in translations:\n", + " # create a Translation instance with title, client, source, target, words, start, deadline, price, tm and domain\n", + " my_translation = Translation(translation['title'], translation['client'], translation['source'], translation['target'], translation['words'], translation['start'], translation['deadline'], translation['price'], translation['tm'], translation['domain'])\n", + " \n", + " # print the project information\n", + " print(my_translation)\n", + " \n", + " # print a separating line between translations\n", + " print('----')" + ] + }, + { + "cell_type": "markdown", + "id": "4fbcdaa6", + "metadata": {}, + "source": [ + "## Expanding and improving the script for a translation agency\n", + "- Actually putting the translator's identity in the printed project information (since the database now contains projects handled by various translators).\n", + "- Adding a `revisor` and `status` attribute.\n", + "- No longer applying the conversion to ISO format of start date and deadline at the instance attribute level, but creating extra computed attributes `self.st` and `self.dl` used only in calculations (makes calling the `start` and `deadline` attributes more user-friendly)." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "728a7f92", + "metadata": {}, + "outputs": [], + "source": [ + "class Translation_agency:\n", + " translator = \"Internal\" # class attribute\n", + " revisor = \"Internal\"\n", + " status = \"created\"\n", + "\n", + " def __init__(self, title, client, source, target, words, start, deadline, price, tm, domain = ''):\n", + " # 'self' represents the object (= class element) itself\n", + " self.title = title\n", + " self.client = client\n", + " self.source = source\n", + " self.target = target\n", + " self.words = words\n", + " self.start = start\n", + " self.deadline = deadline\n", + " self.price = price\n", + " self.tm = tm\n", + " self.domain = domain\n", + " \n", + " today = datetime.date.today() # current date\n", + " self.st = datetime.date.fromisoformat(start) # turns string into date\n", + " self.dl = datetime.date.fromisoformat(deadline) # turns string into date\n", + " self.daysleft = self.dl - today # difference between deadline and current date\n", + " self.length = self.dl - self.st # difference between deadline and start date\n", + " self.rate = self.price/self.words # word rate (price divided by word count)\n", + " self.efficiency = words/self.length.days # words to translate per day (word count divided by project length, see 'length' in explanations above)\n", + "\n", + " def days_left(self):\n", + " # prints a text indicating how many days are left until the project deadline\n", + " if self.dl < datetime.date.today():\n", + " # if the deadline is in the past\n", + " return f\"The deadline has been exceeded already.\"\n", + " else:\n", + " # if the deadline is not in the past\n", + " return f\"There are {self.daysleft.days} days left until the deadline.\"\n", + " \n", + " def project_length(self):\n", + " return f\"{self.length.days} days\"\n", + " \n", + " def __str__(self):\n", + " # defines the print behaviour: returns a text providing the main information about the project\n", + " sent_1 = f\"{self.title} is a translation for {self.client} from {self.source} into {self.target}.\"\n", + " if self.translator == \"Internal\" and self.revisor == \"Internal\":\n", + " sent_2 = f\"Both the translator and the revisor are agency collaborators.\"\n", + " elif self.translator == \"Internal\" and self.revisor != \"Internal\":\n", + " sent_2 = f\"The translator is an agency collaborator and the revisor is {self.revisor}.\"\n", + " elif self.translator != \"Internal\" and self.revisor == \"Internal\":\n", + " sent_2 = f\"The translator is {self.translator} and the revisor is an agency collaborator.\"\n", + " else:\n", + " sent_2 = f\"The translator is {self.translator} and the revisor is {self.revisor}.\"\n", + " # this if-statement considers whether a domain was added\n", + " if len(self.domain) > 0:\n", + " sent_3 = f\"The domain is: {self.domain}.\"\n", + " else:\n", + " sent_3 = \"The domain is unspecified.\" # if no domain was added, the text mentions it\n", + " sent_4 = f\"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word.\" #the word rate is rounded to two decimal places to avoid cumbersomely long numbers\n", + " # this if-statement considers whether the deadline is in the past\n", + " if self.dl < datetime.date.today():\n", + " sent_5 = f\"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day.\" # the efficiency is rounded to units because you can't translate a fraction of a word anyway\n", + " else:\n", + " sent_5 = f\"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day.\"\n", + " # this if-statement considers whether there is a translation memory for the project\n", + " sent_6 = f\"There is {'a' if self.tm else 'no'} translation memory.\"\n", + " sent_7 = f\"The project is currently {self.status}.\"\n", + " # print each sentence in a different line\n", + " return \"\\n\".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7])" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "37cd848e", + "metadata": {}, + "outputs": [], + "source": [ + "rhumatismes_inflammatoires = {\n", + " 'translator' : 'Internal',\n", + " 'revisor' : 'Internal',\n", + " 'status' : 'delivered',\n", + " 'title' : 'La polyarthrite rhumatoïde et autres rhumatismes inflammatoires',\n", + " 'client' : 'Reuma vzw',\n", + " 'source' : 'FR',\n", + " 'target' : 'NL',\n", + " 'words' : 2131,\n", + " 'start' : '2020-09-24',\n", + " 'deadline' : '2020-10-15',\n", + " 'price' : 210,\n", + " 'tm' : False,\n", + " 'domain' : 'healthcare'\n", + "}\n", + "handboek = {\n", + " 'translator' : 'Sibylle',\n", + " 'revisor' : 'Internal',\n", + " 'status' : 'delayed',\n", + " 'title' : 'Handboek voor studentenvertegenwoordigers',\n", + " 'client' : 'KU Leuven',\n", + " 'source' : 'NL',\n", + " 'target' : 'EN',\n", + " 'words' : 3654,\n", + " 'start' : '2023-02-21',\n", + " 'deadline' : '2023-03-02',\n", + " 'price' : 540,\n", + " 'tm' : True,\n", + " 'domain' : 'education'\n", + "}\n", + "user_guide = {\n", + " 'translator' : 'Internal',\n", + " 'revisor' : 'Sibylle',\n", + " 'status' : 'cancelled',\n", + " 'title' : 'User Guide MFPs',\n", + " 'client' : 'UGent',\n", + " 'source' : 'EN',\n", + " 'target' : 'NL',\n", + " 'words' : 1852,\n", + " 'start' : '2023-04-12',\n", + " 'deadline' : '2023-04-14',\n", + " 'price' : 280,\n", + " 'tm' : True,\n", + " 'domain' : ''\n", + "}\n", + "guide_bruxelles = {\n", + " 'translator' : 'Sibylle',\n", + " 'revisor' : 'Natacha',\n", + " 'status' : 'in revision',\n", + " 'title' : 'Guide de Bruxelles',\n", + " 'client' : 'Foodies',\n", + " 'source' : 'NL',\n", + " 'target' : 'FR',\n", + " 'words' : 11500,\n", + " 'start' : '2023-04-06',\n", + " 'deadline' : '2023-05-27',\n", + " 'price' : 1610,\n", + " 'tm' : False,\n", + " 'domain' : ''\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "83aaa188", + "metadata": {}, + "outputs": [], + "source": [ + "translation_projects = [rhumatismes_inflammatoires, handboek, user_guide, guide_bruxelles]" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "d17cc1a8", + "metadata": {}, + "outputs": [], + "source": [ + "with open('translation_agency_projects.json', 'w', encoding='utf-8') as f:\n", + " json.dump(translation_projects, f)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "3e65cbb7", + "metadata": {}, + "outputs": [], + "source": [ + "translation_agency_file = 'translation_agency_projects.json' # assign filename to a string variable\n", + "with open(translation_agency_file, encoding = 'utf-8') as f:\n", + " # open file and use json to parse it\n", + " translations_agency = json.load(f) # translations is now a list of dictionaries. " + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "eb0cc334", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "La polyarthrite rhumatoïde et autres rhumatismes inflammatoires is a translation for Reuma vzw from FR into NL.\n", + "Both the translator and the revisor are agency collaborators.\n", + "The domain is: healthcare.\n", + "It's 2131 words long, with a rate of 0.1 € per word.\n", + "It started on 2020-09-24 and was due on 2020-10-15, so 21 days were foreseen for it. To meet the deadline, 101 words needed to be translated or revised per day.\n", + "There is no translation memory.\n", + "The project is currently delivered.\n", + "----\n", + "Handboek voor studentenvertegenwoordigers is a translation for KU Leuven from NL into EN.\n", + "The translator is Sibylle and the revisor is an agency collaborator.\n", + "The domain is: education.\n", + "It's 3654 words long, with a rate of 0.15 € per word.\n", + "It started on 2023-02-21 and was due on 2023-03-02, so 9 days were foreseen for it. To meet the deadline, 406 words needed to be translated or revised per day.\n", + "There is a translation memory.\n", + "The project is currently delayed.\n", + "----\n", + "User Guide MFPs is a translation for UGent from EN into NL.\n", + "The translator is an agency collaborator and the revisor is Sibylle.\n", + "The domain is unspecified.\n", + "It's 1852 words long, with a rate of 0.15 € per word.\n", + "It started on 2023-04-12 and was due on 2023-04-14, so 2 days were foreseen for it. To meet the deadline, 926 words needed to be translated or revised per day.\n", + "There is a translation memory.\n", + "The project is currently cancelled.\n", + "----\n", + "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", + "The translator is Sibylle and the revisor is Natacha.\n", + "The domain is unspecified.\n", + "It's 11500 words long, with a rate of 0.14 € per word.\n", + "It started on 2023-04-06 and is due on 2023-05-27, so 51 days are foreseen for it, of which 5 left. To meet the deadline, 225 words need to be translated or revised per day.\n", + "There is no translation memory.\n", + "The project is currently in revision.\n", + "----\n" + ] + } + ], + "source": [ + "# go through each of the items in the list\n", + "for translation in translations_agency:\n", + " # create a Translation instance with title, client, source, target, words, start, deadline, price, tm and domain\n", + " my_translation2 = Translation_agency(translation['title'], translation['client'], translation['source'], translation['target'], translation['words'], translation['start'], translation['deadline'], translation['price'], translation['tm'], translation['domain'])\n", + " if translation['translator'] != \"Internal\":\n", + " my_translation2.translator = translation['translator']\n", + " if translation['revisor'] != \"Internal\":\n", + " my_translation2.revisor = translation['revisor']\n", + " if translation['status'] != \"created\":\n", + " my_translation2.status = translation['status'] \n", + " # print the project information\n", + " print(my_translation2)\n", + " \n", + " # print a separating line between translations\n", + " print('----')" + ] + }, + { + "cell_type": "markdown", + "id": "bb384876", + "metadata": {}, + "source": [ + "## Left to do\n", + "- Add validation when updating the status: only allow 'created', 'in translation', 'in revision', 'delivered', 'delayed' and 'cancel(l)ed'.\n", + "- Add validation for all the other attributes (not accept 'internal' for translator and revisor, only 'Internal').\n", + "- Create a second class `Freelancers` fed by a json-file containing a freelancer database and use references to the database rather than strings (names) for the name of external translators and revisors (__how ?__).\n", + " - Last name\n", + " - First name\n", + " - Phone number\n", + " - E-mail address\n", + " - Project count\n", + "- Use regex to check e-mail address and phone number in the freelancer database.\n", + "- Implement argparse.\n", + "- Document the script with docstrings." + ] + }, + { + "cell_type": "markdown", + "id": "531f42ed", + "metadata": {}, + "source": [ + "# Extra: Source and target text aligner\n", + "Sometimes, you still have some translations left over from a time where you didn't use CAT-tools and you'd like to feed them into your translation memory. Some CAT-tools have built-in text aligners, but not all of them, so how do you go from two separate text documents to an aligned bilingual (csv-)file ready to be fed into your TM?\n", + "\n", + "## Step one: Prepare the source and target text\n", + "The easiest file format to start from is a pure txt-file... and since for a TM only the pure text is of interest, converting a Word-, PowerPoint- or whatever file to a txt-file isn't an issue. So, we'll take the original source and target document and export them to a txt-format (with utf-8 encoding).\n", + "\n", + "## Step two: Store the two (continuous) texts into variables" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "4150607b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Introduction to Machine Learning with Python.\\n\\nThis module provides an introduction to the basic concepts and use of the Python programming language in support of translation. Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.\\n'" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Source text\n", + "f = open('python_en.txt', encoding = 'utf-8')\n", + "st_1 = f.read()\n", + "f.close()\n", + "st_1" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "ee31b753", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Introduction au machine learning à l’aide de Python.\\n\\nCe module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.\\n'" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Source text\n", + "f = open('python_fr.txt', encoding = 'utf-8')\n", + "tt_1 = f.read()\n", + "f.close()\n", + "tt_1" + ] + }, + { + "cell_type": "markdown", + "id": "bb6de39e", + "metadata": {}, + "source": [ + "## Step three: Split the single text string into list of sentences\n", + "Since most TMs (and CAT-tools) use sentence segmentation, the source and target text need to be split up into sentences. So, each text becomes a list of separate sentences.\n", + "\n", + "For this, we use `nltk tokenizer`, which functions with English and French (and many other languages, but English and French are the ones that interest us right now)." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "b6d77857", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Introduction to Machine Learning with Python.',\n", + " 'This module provides an introduction to the basic concepts and use of the Python programming language in support of translation.',\n", + " 'Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.']" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Source text\n", + "from nltk.tokenize import sent_tokenize\n", + "split_st_1 = sent_tokenize(st_1, language = 'english')\n", + "split_st_1" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "1cca97c2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Introduction au machine learning à l’aide de Python.',\n", + " 'Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction.',\n", + " 'L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.']" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Target text\n", + "from nltk.tokenize import sent_tokenize\n", + "split_tt_1 = sent_tokenize(tt_1, language = 'french')\n", + "split_tt_1" + ] + }, + { + "cell_type": "markdown", + "id": "af933044", + "metadata": {}, + "source": [ + "## Step four: Aligning those lists and exporting the tuples list to a csv-file\n", + "For this step, we'll need the `csv` module:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "d720a8c3", + "metadata": {}, + "outputs": [], + "source": [ + "import csv" + ] + }, + { + "cell_type": "markdown", + "id": "3649fd6e", + "metadata": {}, + "source": [ + "A csv-file consists of rows, often a first header row with the label of each column, followed by the actual content of the file. So, in the first two lines we need to define what each row will contain.\n", + "- The content of the header row (stored in the variable `header`) simply consists of the language codes of the source and target language.\n", + "- The content of the next rows (stored in the variable `tm_1`) contains our texts.\n", + " - The `zip()`-function aligns the first sentence of the source text with the first sentence of the target text, the second with the second... and so on.\n", + " - The `list()`-function ensures that the `tm_1`-variable contains a list of tuples, not a generator (because the `zip()`-function creates a generator).\n", + " \n", + "Once we know what will go into the file, it's time to actually write the file.\n", + "- We open a new file, which gets a name, the `'w'`-command (meaning that it's meant to write a file) and an encoding (utf-8).\n", + "- Then, we define a csv-writer (very creatively called `write`).\n", + "- Lastly, we write the first row (the header) followed by the rest (the actual TM)." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "54465bd4", + "metadata": {}, + "outputs": [], + "source": [ + "header = ['EN', 'FR'] # header row\n", + "tm_1 = list(zip(split_st_1, split_tt_1)) # rest of the file\n", + "\n", + "with open('translation-memory_1.csv', 'w', encoding = 'utf-8') as f:\n", + " write = csv.writer(f)\n", + " \n", + " write.writerow(header)\n", + " write.writerows(tm_1)" + ] + }, + { + "cell_type": "markdown", + "id": "aa373dde", + "metadata": {}, + "source": [ + "## Step five: Admiring our work\n", + "Using pandas, we can read the newly created csv-file." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "dbbcea9d", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "ac2fe189", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ENFR
0Introduction to Machine Learning with Python.Introduction au machine learning à l’aide de P...
1This module provides an introduction to the ba...Ce module offre une introduction aux concepts ...
2Focus lies on the main concepts that include N...L’accent est mis sur le traitement du langage ...
\n", + "
" + ], + "text/plain": [ + " EN \\\n", + "0 Introduction to Machine Learning with Python. \n", + "1 This module provides an introduction to the ba... \n", + "2 Focus lies on the main concepts that include N... \n", + "\n", + " FR \n", + "0 Introduction au machine learning à l’aide de P... \n", + "1 Ce module offre une introduction aux concepts ... \n", + "2 L’accent est mis sur le traitement du langage ... " + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "read_tm = pd.read_csv('translation-memory_1.csv', sep = ',')\n", + "read_tm.head()" + ] + }, + { + "cell_type": "markdown", + "id": "dca298fe", + "metadata": {}, + "source": [ + "(Next step: figuring out how to make pandas display the whole text.)" + ] + }, + { + "cell_type": "markdown", + "id": "9ddb159f", + "metadata": {}, + "source": [ + "## Step six: Importing the csv-file into a CAT-tool TM" + ] + }, + { + "cell_type": "markdown", + "id": "2c456496", + "metadata": {}, + "source": [ + "(I would need to swtich to Windows to show that.)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/python_en.txt b/python_en.txt new file mode 100644 index 0000000..93484cf --- /dev/null +++ b/python_en.txt @@ -0,0 +1 @@ +Introduction to Machine Learning with Python. This module provides an introduction to the basic concepts and use of the Python programming language in support of translation. Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning. \ No newline at end of file diff --git a/python_fr.txt b/python_fr.txt new file mode 100644 index 0000000..c969aa6 --- /dev/null +++ b/python_fr.txt @@ -0,0 +1 @@ +Introduction au machine learning à l’aide de Python. Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning. \ No newline at end of file diff --git a/translation-memory_1.csv b/translation-memory_1.csv index 4ed4e54..ae72263 100644 --- a/translation-memory_1.csv +++ b/translation-memory_1.csv @@ -1,3 +1,4 @@ +EN,FR Introduction to Machine Learning with Python.,Introduction au machine learning à l’aide de Python. This module provides an introduction to the basic concepts and use of the Python programming language in support of translation.,Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. "Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.","L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning." diff --git a/translation_agency_projects.json b/translation_agency_projects.json new file mode 100644 index 0000000..211e436 --- /dev/null +++ b/translation_agency_projects.json @@ -0,0 +1 @@ +[{"translator": "Internal", "revisor": "Internal", "status": "delivered", "title": "La polyarthrite rhumato\u00efde et autres rhumatismes inflammatoires", "client": "Reuma vzw", "source": "FR", "target": "NL", "words": 2131, "start": "2020-09-24", "deadline": "2020-10-15", "price": 210, "tm": false, "domain": "healthcare"}, {"translator": "Sibylle", "revisor": "Internal", "status": "delayed", "title": "Handboek voor studentenvertegenwoordigers", "client": "KU Leuven", "source": "NL", "target": "EN", "words": 3654, "start": "2023-02-21", "deadline": "2023-03-02", "price": 540, "tm": true, "domain": "education"}, {"translator": "Internal", "revisor": "Sibylle", "status": "cancelled", "title": "User Guide MFPs", "client": "UGent", "source": "EN", "target": "NL", "words": 1852, "start": "2023-04-12", "deadline": "2023-04-14", "price": 280, "tm": true, "domain": ""}, {"translator": "Sibylle", "revisor": "Natacha", "status": "in revision", "title": "Guide de Bruxelles", "client": "Foodies", "source": "NL", "target": "FR", "words": 11500, "start": "2023-04-06", "deadline": "2023-05-27", "price": 1610, "tm": false, "domain": ""}] \ No newline at end of file diff --git a/translation_projects.json b/translation_projects.json new file mode 100644 index 0000000..70522a0 --- /dev/null +++ b/translation_projects.json @@ -0,0 +1 @@ +[{"title": "La polyarthrite rhumato\u00efde et autres rhumatismes inflammatoires", "client": "Reuma vzw", "source": "FR", "target": "NL", "words": 2131, "start": "2020-09-24", "deadline": "2020-10-15", "price": 210, "tm": false, "domain": "healthcare"}, {"title": "Handboek voor studentenvertegenwoordigers", "client": "KU Leuven", "source": "NL", "target": "EN", "words": 3654, "start": "2023-02-21", "deadline": "2023-03-02", "price": 540, "tm": true, "domain": "education"}, {"title": "User Guide MFPs", "client": "UGent", "source": "EN", "target": "NL", "words": 1852, "start": "2023-04-14", "deadline": "2023-04-16", "price": 280, "tm": true, "domain": ""}, {"title": "Guide de Bruxelles", "client": "Foodies", "source": "NL", "target": "FR", "words": 11500, "start": "2023-03-22", "deadline": "2023-05-06", "price": 1610, "tm": false, "domain": ""}] \ No newline at end of file From b75a20cf5e99aff5e30ab3aca68dcc57e83a3b33 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Fri, 2 Jun 2023 14:53:39 +0200 Subject: [PATCH 13/46] Cleaning up repository Removing files that are no longer necessary --- Translation-technology_TMexample.tmx | Bin 49376 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Translation-technology_TMexample.tmx diff --git a/Translation-technology_TMexample.tmx b/Translation-technology_TMexample.tmx deleted file mode 100644 index d3266338364288b77210b0f73b5cc416d1843db4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 49376 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 NouS(0=>0Xa_y45EI`04g From dac993fa649244f7da8e44de25bed23b2d69516b Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Fri, 2 Jun 2023 14:54:23 +0200 Subject: [PATCH 14/46] Cleaning up repository Removing the files that are no longer necessary. --- Translation-technology_TBexample.xml | Bin 72188 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Translation-technology_TBexample.xml diff --git a/Translation-technology_TBexample.xml b/Translation-technology_TBexample.xml deleted file mode 100644 index 9daff3d9984db63f82938fe013737554805ea7a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 72188 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 From 766d863c4480035386f1faf4a8a7ef54d5e3de2d Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Fri, 2 Jun 2023 14:54:58 +0200 Subject: [PATCH 15/46] Cleaning up repository Removing the files that are no longer necessary. --- Translation-technology_TBexample.xdt | 1217 -------------------------- 1 file changed, 1217 deletions(-) delete mode 100644 Translation-technology_TBexample.xdt diff --git a/Translation-technology_TBexample.xdt b/Translation-technology_TBexample.xdt deleted file mode 100644 index c3746b7..0000000 --- a/Translation-technology_TBexample.xdt +++ /dev/null @@ -1,1217 +0,0 @@ - - - - - - Entry level - 0 - 1 - 1 - 0 - - - - Index level - 2 - 1 - 1 - 0 - - - - Term level - 3 - 1 - 1 - 0 - - - - Usage example - 1 - 0 - 0 - 0 - - - - - Part of speech - 1 - 0 - 0 - 0 - - - - - Number (grammar) - 1 - 0 - 0 - 0 - - - - - Gender (grammar) - 1 - 0 - 0 - 0 - - - - - Definition - 1 - 0 - 0 - 0 - - - - - Status - 1 - 0 - 0 - 1 - - - - - - Definition - 1 - 0 - 0 - 0 - - - - - - Client - 1 - 0 - 0 - 0 - - - - - Domain - 1 - 0 - 0 - 0 - - - - - ID - 1 - 0 - 0 - 0 - - - - - Note - 1 - 0 - 0 - 0 - - - - - Project - 1 - 0 - 0 - 0 - - - - - Subject - 1 - 0 - 0 - 0 - - - - - Image - 1 - 0 - 0 - 0 - - - - - - - NL - Dutch - EN - English - FR - French - - - - ID - - Number - F - - - - Note - - Text - F - - - - Project - - Text - F - - - - Client - - Text - F - - - - Domain - - Text - F - - - - Subject - - Text - F - - - - Definition - - Text - F - - - - Usage example - - Text - F - - - - Status - - Picklist - F - Forbidden - None - - - - Part of speech - - Picklist - F - N - Adj - Adv - V - X - - - - Number (grammar) - - Picklist - F - Sing - Pl - - - - Gender (grammar) - - Picklist - F - Masc - Fem - N - - - - Image - - Multimedia File - F - - - - - 0 - 0 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - From ab372aa1ed16177e3e7473c3907e9bcf3815010a Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Fri, 2 Jun 2023 14:55:20 +0200 Subject: [PATCH 16/46] Cleaning up repository Removing the files that are no longer necessary. --- Translation-technology_TBexample.xdl | Bin 358488 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Translation-technology_TBexample.xdl diff --git a/Translation-technology_TBexample.xdl b/Translation-technology_TBexample.xdl deleted file mode 100644 index 0eff8c015a83061d994b44fe5d7c960766f707e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 358488 zcmeHw`*R$}k!Jr&NBDoh@*JiVyL{_yh---yW!;GsPo!kuc{dOs2$HbzG5|{C?%n
a5+B*wvy`i2R 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 b(@?AYiaOECBkIpS^$5EvIA+k1SZe%#J~lV= From 94eb734b28f6efe49c285e2def7c78bf534e851f Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Fri, 2 Jun 2023 14:55:49 +0200 Subject: [PATCH 17/46] Cleaning up repository Removing the files that are no longer necessary. --- Translation-technology_TBexample.csv | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 Translation-technology_TBexample.csv diff --git a/Translation-technology_TBexample.csv b/Translation-technology_TBexample.csv deleted file mode 100644 index 5568dcf..0000000 --- a/Translation-technology_TBexample.csv +++ /dev/null @@ -1,24 +0,0 @@ -Entry_ID,Entry_Subject,Entry_Domain,Entry_ClientID,Entry_ProjectID,Entry_Created,Entry_Creator,English,French -0,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:40:45 PM,Sibylle,machine translation,traduction automatique -1,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:43:07 PM,Sibylle,advanced topics,sujets avancés -2,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:43:42 PM,Sibylle,translator,traducteur -3,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:43:58 PM,Sibylle,localiser,localisateur -4,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:44:10 PM,Sibylle,revisor,réviseur -5,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:44:27 PM,Sibylle,website,site web -6,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:44:43 PM,Sibylle,software,software -7,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:44:53 PM,Sibylle,game,jeu -8,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:45:02 PM,Sibylle,subtitler,sous-titreur -9,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:45:12 PM,Sibylle,post-editor,post-éditeur -10,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:45:40 PM,Sibylle,technical writer,rédacteur technique -11,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:46:02 PM,Sibylle,computational linguist,linguiste informatique -12,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:46:15 PM,Sibylle,translation technology,technologies de la traduction -13,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:46:34 PM,Sibylle,work placement,stage -14,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:48:18 PM,Sibylle,data,données -15,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:50:06 PM,Sibylle,statistical,statistique -16,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:50:31 PM,Sibylle,neural,neuronal -17,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:50:41 PM,Sibylle,hybrid,hybride -18,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:50:51 PM,Sibylle,adaptive,adaptatif -19,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:53:07 PM,Sibylle,MT,traduction automatique -20,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:53:31 PM,Sibylle,MT engine,engin de traduction automatique -21,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:54:20 PM,Sibylle,post-editing,post-édition -22,Postgraduate programme translation technology,Translation technology,KU Leuven,Intro to Python,5/8/2023 3:55:51 PM,Sibylle,pre-editing,pré-édition From 13698b7ffcb165dfc6ff9f229353bc30ea778d6b Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Fri, 2 Jun 2023 14:56:16 +0200 Subject: [PATCH 18/46] Cleaning up repository Removing the files that are no longer necessary. --- translation-memory_2.csv | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 translation-memory_2.csv diff --git a/translation-memory_2.csv b/translation-memory_2.csv deleted file mode 100644 index ae72263..0000000 --- a/translation-memory_2.csv +++ /dev/null @@ -1,4 +0,0 @@ -EN,FR -Introduction to Machine Learning with Python.,Introduction au machine learning à l’aide de Python. -This module provides an introduction to the basic concepts and use of the Python programming language in support of translation.,Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. -"Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.","L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning." From d27a61797f8b27f0c57648a86c866f29f01e09ad Mon Sep 17 00:00:00 2001 From: Sibylle Date: Fri, 2 Jun 2023 17:15:42 +0200 Subject: [PATCH 19/46] first script final assignment --- script.py | 193 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 3 deletions(-) diff --git a/script.py b/script.py index ac747da..6feb64a 100644 --- a/script.py +++ b/script.py @@ -1,11 +1,198 @@ #! /usr/bin/python -"""Brief description of the script. +import datetime +import re + +"""This script gives a translation agency an overview of all its projects. """ import argparse -# write here your function and class definitions -# don't forget to properly document them with docstrings!! +class Project: + + def __init__(self, title, client, source, target, words, start, deadline, price, tm, translator = 'internal', revisor = 'internal', status = 'created', domain = ''): + """Initialises an object of the Project class, a class that represents a translation project of a translation agency. + + Args: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + translator (string, optional): Translator assigned to the project. Defaults to 'internal'. + revisor (string, optional): Revisor assigned to the project. Defaults to 'internal'. + status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'. + domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string. + + Attributes: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + domain (str): Overall domain to which the project belongs. + today (date): Date of the day where the script is run. + st (date): Project's start date, turned from an ISO-formatted string into a date. + dl (date): Project's deadline, turned from an ISO-formatted string into a date. + daysleft (timedelta): Time left until the project's deadline. + length (timedelta): Total time allocated to the project. + rate (float): Project's word rate. + efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek). + + Raises: + TypeError: If 'title' is not a string. + TypeError: If 'client' is not a string. + TypeError: If 'source' is not a string. + TypeError: If 'target' is not a string. + TypeError: If 'words' is not an integer. + TypeError: If 'start' is not a string. + ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'deadline' is not a string. + ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'price' is not a float. + TypeError: If 'tm' is not a boolean. + TypeError: If 'translator' is not a string. + TypeError: If 'revisor' is not a string. + TypeError: If 'status' is not a string. + ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed. + TypeError: If 'domain' is not a string. + """ + if type(title) != str: + raise TypeError("The title must be a string.") + else: + self.title = title + if type(client) != str: + raise TypeError("The client name must be a string.") + else: + self.client = client + if type(source) != str: + raise TypeError("The source language must be a string.") + else: + self.source = source + if type(target) != str: + raise TypeError("The target langague must be a string.") + else: + self.target = target + if type(words) != int: + raise TypeError("The word count must be an integer.") + else: + self.words = words + if type(start) != str: + raise TypeError("The start date must be provided as a string.") + elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", start): + raise ValueError("A valid start date must be provided in ISO format.") + else: + self.start = start + if type(deadline) != str: + raise TypeError("The deadline must be provided as a string.") + elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", deadline): + raise ValueError("A valid deadline must be provided in ISO format.") + else: + self.deadline = deadline + if type(price) != float: + raise TypeError("The price must be a float.") + else: + self.price = price + if type(tm) != bool: + raise TypeError("The TM availability must be a boolean.") + else: + self.tm = tm + if type(translator) != str: + raise TypeError("The translator's name must be a string.") + else: + self.translator = translator + if type(revisor) != str: + raise TypeError("The revisor's name must be a string.") + else: + self.revisor = revisor + if type(status) != str: + raise TypeError("The status must be a string.") + elif not status.lower() in ["created", + "in translation", + "in revision", + "delivered", + "delayed", + "cancelled", + "canceled"]: + raise ValueError("Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.") + else: + self.status = status + if type(domain) != str: + raise TypeError("The domain must be a string.") + else: + self.domain = domain + + today = datetime.date.today() + self.st = datetime.date.fromisoformat(start) + self.dl = datetime.date.fromisoformat(deadline) + self.daysleft = self.dl - today + self.length = self.dl - self.st + self.rate = self.price/self.words + self.efficiency = words/((5/7)*self.length.days) + + def days_left(self): + """Displays how many days remain until the project deadline. + + Args: + None + + Returns: + str: A message informing the user that the deadline has been exceeded already if the deadline is in the past. + str: A message informing the user of the number of days left until the deadline if the deadline is not in the past. + """ + # prints a text indicating how many days are left until the project deadline + if self.dl < datetime.date.today(): + # if the deadline is in the past + return f"The deadline has been exceeded already." + else: + # if the deadline is not in the past + return f"There are {self.daysleft.days} days left until the deadline." + + def project_length(self): + """Displays the total number of days allocated to the project. + + Args: + None + + Returns: + str: The total number of days allocated to the project. + """ + return f"{self.length.days} days" + + def __str__(self): + # defines the print behaviour: returns a text providing the main information about the project + sent_1 = f"{self.title} is a translation for {self.client} from {self.source} into {self.target}." + if self.translator == "internal" and self.revisor == "internal": + sent_2 = f"Both the translator and the revisor are agency collaborators." + elif self.translator == "internal" and self.revisor != "internal": + sent_2 = f"The translator is an agency collaborator and the revisor is {self.revisor}." + elif self.translator != "internal" and self.revisor == "internal": + sent_2 = f"The translator is {self.translator} and the revisor is an agency collaborator." + else: + sent_2 = f"The translator is {self.translator} and the revisor is {self.revisor}." + # this if-statement considers whether a domain was added + if len(self.domain) > 0: + sent_3 = f"The domain is: {self.domain}." + else: + sent_3 = "The domain is unspecified." # if no domain was added, the text mentions it + sent_4 = f"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word." #the word rate is rounded to two decimal places to avoid cumbersomely long numbers + # this if-statement considers whether the deadline is in the past + if self.dl < datetime.date.today(): + sent_5 = f"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day." # the efficiency is rounded to units because you can't translate a fraction of a word anyway + else: + sent_5 = f"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day." + # this if-statement considers whether there is a translation memory for the project + sent_6 = f"There is {'a' if self.tm else 'no'} translation memory." + sent_7 = f"The project is currently {self.status}." + # print each sentence in a different line + return "\n".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7]) if __name__ == "__main": # write here what happens if the code is run instead of imported From 4fd92f9c46251bc88274ffe6f723a8499059e8e0 Mon Sep 17 00:00:00 2001 From: Sibylle Date: Fri, 2 Jun 2023 18:53:10 +0200 Subject: [PATCH 20/46] update translator, revisor and status validation --- script.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/script.py b/script.py index 6feb64a..d35f31d 100644 --- a/script.py +++ b/script.py @@ -106,14 +106,20 @@ def __init__(self, title, client, source, target, words, start, deadline, price, self.tm = tm if type(translator) != str: raise TypeError("The translator's name must be a string.") + elif translator == '': + self.translator = "internal" else: self.translator = translator if type(revisor) != str: raise TypeError("The revisor's name must be a string.") + elif revisor == '': + self.revisor = "internal" else: self.revisor = revisor if type(status) != str: raise TypeError("The status must be a string.") + elif status == '': + self.status = "created" elif not status.lower() in ["created", "in translation", "in revision", From fdca814cec14cd45178378afe414f6693829701f Mon Sep 17 00:00:00 2001 From: Sibylle Date: Fri, 2 Jun 2023 18:56:29 +0200 Subject: [PATCH 21/46] adding argparse to the script, attempt 1 --- script-with-argparse.py | 223 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 script-with-argparse.py diff --git a/script-with-argparse.py b/script-with-argparse.py new file mode 100644 index 0000000..7e99e81 --- /dev/null +++ b/script-with-argparse.py @@ -0,0 +1,223 @@ +#! /usr/bin/python + +import datetime +import re + +"""This script gives a translation agency an overview of all its projects. +""" +import argparse + +class Project: + + def __init__(self, title, client, source, target, words, start, deadline, price, tm, translator = 'internal', revisor = 'internal', status = 'created', domain = ''): + """Initialises an object of the Project class, a class that represents a translation project of a translation agency. + + Args: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + translator (string, optional): Translator assigned to the project. Defaults to 'internal'. + revisor (string, optional): Revisor assigned to the project. Defaults to 'internal'. + status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'. + domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string. + + Attributes: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + domain (str): Overall domain to which the project belongs. + today (date): Date of the day where the script is run. + st (date): Project's start date, turned from an ISO-formatted string into a date. + dl (date): Project's deadline, turned from an ISO-formatted string into a date. + daysleft (timedelta): Time left until the project's deadline. + length (timedelta): Total time allocated to the project. + rate (float): Project's word rate. + efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek). + + Raises: + TypeError: If 'title' is not a string. + TypeError: If 'client' is not a string. + TypeError: If 'source' is not a string. + TypeError: If 'target' is not a string. + TypeError: If 'words' is not an integer. + TypeError: If 'start' is not a string. + ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'deadline' is not a string. + ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'price' is not a float. + TypeError: If 'tm' is not a boolean. + TypeError: If 'translator' is not a string. + TypeError: If 'revisor' is not a string. + TypeError: If 'status' is not a string. + ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed. + TypeError: If 'domain' is not a string. + """ + if type(title) != str: + raise TypeError("The title must be a string.") + else: + self.title = title + if type(client) != str: + raise TypeError("The client name must be a string.") + else: + self.client = client + if type(source) != str: + raise TypeError("The source language must be a string.") + else: + self.source = source + if type(target) != str: + raise TypeError("The target langague must be a string.") + else: + self.target = target + if type(words) != int: + raise TypeError("The word count must be an integer.") + else: + self.words = words + if type(start) != str: + raise TypeError("The start date must be provided as a string.") + elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", start): + raise ValueError("A valid start date must be provided in ISO format.") + else: + self.start = start + if type(deadline) != str: + raise TypeError("The deadline must be provided as a string.") + elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", deadline): + raise ValueError("A valid deadline must be provided in ISO format.") + else: + self.deadline = deadline + if type(price) != float: + raise TypeError("The price must be a float.") + else: + self.price = price + if type(tm) != bool: + raise TypeError("The TM availability must be a boolean.") + else: + self.tm = tm + if type(translator) != str: + raise TypeError("The translator's name must be a string.") + elif translator == '': + self.translator = "internal" + else: + self.translator = translator + if type(revisor) != str: + raise TypeError("The revisor's name must be a string.") + elif revisor == '': + self.revisor = "internal" + else: + self.revisor = revisor + if type(status) != str: + raise TypeError("The status must be a string.") + elif status == '': + self.status = "created" + elif not status.lower() in ["created", + "in translation", + "in revision", + "delivered", + "delayed", + "cancelled", + "canceled"]: + raise ValueError("Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.") + else: + self.status = status + if type(domain) != str: + raise TypeError("The domain must be a string.") + else: + self.domain = domain + + today = datetime.date.today() + self.st = datetime.date.fromisoformat(start) + self.dl = datetime.date.fromisoformat(deadline) + self.daysleft = self.dl - today + self.length = self.dl - self.st + self.rate = self.price/self.words + self.efficiency = words/((5/7)*self.length.days) + + def days_left(self): + """Displays how many days remain until the project deadline. + + Args: + None + + Returns: + str: A message informing the user that the deadline has been exceeded already if the deadline is in the past. + str: A message informing the user of the number of days left until the deadline if the deadline is not in the past. + """ + # prints a text indicating how many days are left until the project deadline + if self.dl < datetime.date.today(): + # if the deadline is in the past + return f"The deadline has been exceeded already." + else: + # if the deadline is not in the past + return f"There are {self.daysleft.days} days left until the deadline." + + def project_length(self): + """Displays the total number of days allocated to the project. + + Args: + None + + Returns: + str: The total number of days allocated to the project. + """ + return f"{self.length.days} days" + + def __str__(self): + # defines the print behaviour: returns a text providing the main information about the project + sent_1 = f"{self.title} is a translation for {self.client} from {self.source} into {self.target}." + if self.translator == "internal" and self.revisor == "internal": + sent_2 = f"Both the translator and the revisor are agency collaborators." + elif self.translator == "internal" and self.revisor != "internal": + sent_2 = f"The translator is an agency collaborator and the revisor is {self.revisor}." + elif self.translator != "internal" and self.revisor == "internal": + sent_2 = f"The translator is {self.translator} and the revisor is an agency collaborator." + else: + sent_2 = f"The translator is {self.translator} and the revisor is {self.revisor}." + # this if-statement considers whether a domain was added + if len(self.domain) > 0: + sent_3 = f"The domain is: {self.domain}." + else: + sent_3 = "The domain is unspecified." # if no domain was added, the text mentions it + sent_4 = f"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word." #the word rate is rounded to two decimal places to avoid cumbersomely long numbers + # this if-statement considers whether the deadline is in the past + if self.dl < datetime.date.today(): + sent_5 = f"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day." # the efficiency is rounded to units because you can't translate a fraction of a word anyway + else: + sent_5 = f"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day." + # this if-statement considers whether there is a translation memory for the project + sent_6 = f"There is {'a' if self.tm else 'no'} translation memory." + sent_7 = f"The project is currently {self.status}." + # print each sentence in a different line + return "\n".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7]) + +if __name__ == "__main": + parser = argparse.ArgumentParser("project") + parser.add_argument("title", type=str, help="Title of the project") + parser.add_argument("client", type=str, help="Client who ordered the translation") + parser.add_argument("source", type=str, help="Source language") + parser.add_argument("target", type=str, help="Target language") + parser.add_argument("words", type=int, help="Project word count") + parser.add_argument("start", type=str, help="Project start date") + parser.add_argument("deadline", type=str, help="Project deadline") + parser.add_argument("price", type=float, help="Total price project") + parser.add_argument("tm", type=bool, help="Whether a translation memory is available") + parser.add_argument("--translator", type=str, default = "internal", help="Translator assigned to the project") + parser.add_argument("--revisor", type=str, default = "internal",help="Revisor assigned to the project") + parser.add_argument("--status", type=str, default = "created", + choices = ["created", "in translation", "in revision", "delivered", "delayed", "cancelled", "canceled"], help="Project status in the agency workflow") + parser.add_argument("--domain", type=str, default = "", help="Overall domain of the text") + args = parser.parse_args() + proj = Project(args.title, args.client, args.source, args.target, args.words, args.start, args.deadline, args.price, args.tm, args.translator, args.revisor, args.status, args.domain) + proj.days_left() + proj.project_length() + \ No newline at end of file From 183b52d9a52d3ea9354a2db1a7fc38104d91558d Mon Sep 17 00:00:00 2001 From: Sibylle Date: Fri, 2 Jun 2023 19:30:00 +0200 Subject: [PATCH 22/46] adding the TM generator tool --- TM-generator.md | 153 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 TM-generator.md diff --git a/TM-generator.md b/TM-generator.md new file mode 100644 index 0000000..eab89b0 --- /dev/null +++ b/TM-generator.md @@ -0,0 +1,153 @@ +# Source and target text aligner +Sometimes, you still have some translations left over from a time where you didn't use CAT-tools and you'd like to feed them into your translation memory. Some CAT-tools have built-in text aligners, but not all of them, so how do you go from two separate text documents to an aligned bilingual (csv-)file ready to be fed into your TM? + +## Step one: Prepare the source and target text +The easiest file format to start from is a pure txt-file... and since for a TM only the pure text is of interest, converting a Word-, PowerPoint- or whatever file to a txt-file isn't an issue. So, we'll take the original source and target document and export them to a txt-format (with utf-8 encoding). +## Step two: Store the two (continuous) texts into variables + + +```python +with open('python_en.txt', encoding = 'utf-8') as f: + st_1 = f.read() +st_1 +``` + + + + + 'Introduction to Machine Learning with Python.\n\nThis module provides an introduction to the basic concepts and use of the Python programming language in support of translation. Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.\n' + + + + +```python +with open('python_fr.txt', encoding = 'utf-8') as f: + tt_1 = f.read() +tt_1 +``` + + + + + 'Introduction au machine learning à l’aide de Python.\n\nCe module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.\n' + + + +## Step three: Split the single text string into list of sentences +Since most TMs (and CAT-tools) use sentence segmentation, the source and target text need to be split up into sentences. So, each text becomes a list of separate sentences. + +For this, we use `nltk tokenizer`, which functions with English and French (and many other languages, but English and French are the ones that interest us right now). + + +```python +from nltk.tokenize import sent_tokenize +split_st_1 = sent_tokenize(st_1, language = 'english') +split_st_1 +``` + + + + + ['Introduction to Machine Learning with Python.', + 'This module provides an introduction to the basic concepts and use of the Python programming language in support of translation.', + 'Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.'] + + + + +```python +from nltk.tokenize import sent_tokenize +split_tt_1 = sent_tokenize(tt_1, language = 'french') +split_tt_1 +``` + + + + + ['Introduction au machine learning à l’aide de Python.', + 'Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction.', + 'L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.'] + + + +## Step four: Aligning those lists and exporting the tuples list to a csv-file +Writing a csv-file looks fairly similar to reading a txt-file, like we did at the start of the process, except this time we use `f.write` instead of `f.read`. +A csv-file consists of rows, often a first header row with the label of each column, followed by the actual content of the file. Both are defined separately. +- The content of the header row (defined in the first indented line) simply consists of the language codes of the source and target language. +- The content of the next rows (defined in the next indented lines) contains our texts. + - The `zip()`-function aligns the first sentence of the source text with the first sentence of the target text, the second with the second... and so on in x, y tuples. + - Next, those tuples are put inside an f-string that separates x and y with a tab and places makes a new line start after each y. + + +```python +with open("translation_memory.csv", "w", encoding = "utf-8") as f: # open file to overwrite + f.write("EN\tFR\n") # write heading + for x, y in zip(split_st_1, split_tt_1): # go through each pair of lines + f.write(f"{x}\t{y}\n") # write the line +``` + +## Step five: Admiring our work +Using pandas, we can read the newly created csv-file. + + +```python +import pandas as pd +``` + + +```python +read_tm = pd.read_csv('translation_memory.csv', sep = '\t') +read_tm.head() +``` + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
ENFR
0Introduction to Machine Learning with Python.Introduction au machine learning à l’aide de P...
1This module provides an introduction to the ba...Ce module offre une introduction aux concepts ...
2Focus lies on the main concepts that include N...L’accent est mis sur le traitement du langage ...
+
+ + + + +```python + +``` From 69f30d7fb79b813f1352d6335ecbd7dbd6d61f3e Mon Sep 17 00:00:00 2001 From: Sibylle Date: Sat, 3 Jun 2023 16:35:30 +0200 Subject: [PATCH 23/46] revisor -> reviewer, internal lowercase --- script.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/script.py b/script.py index d35f31d..3d28a19 100644 --- a/script.py +++ b/script.py @@ -9,7 +9,7 @@ class Project: - def __init__(self, title, client, source, target, words, start, deadline, price, tm, translator = 'internal', revisor = 'internal', status = 'created', domain = ''): + def __init__(self, title, client, source, target, words, start, deadline, price, tm, translator = 'internal', reviewer = 'internal', status = 'created', domain = ''): """Initialises an object of the Project class, a class that represents a translation project of a translation agency. Args: @@ -23,7 +23,7 @@ def __init__(self, title, client, source, target, words, start, deadline, price, price (float): Total price invoiced to the client (excl. VAT). tm (bool): Whether a translation memory is available for this project. translator (string, optional): Translator assigned to the project. Defaults to 'internal'. - revisor (string, optional): Revisor assigned to the project. Defaults to 'internal'. + reviewer (string, optional): Reviewer assigned to the project. Defaults to 'internal'. status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'. domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string. @@ -37,6 +37,9 @@ def __init__(self, title, client, source, target, words, start, deadline, price, deadline (str): Project's deadline in ISO format (YYYY-MM-DD). price (float): Total price invoiced to the client (excl. VAT). tm (bool): Whether a translation memory is available for this project. + translator (string, optional): Translator assigned to the project. + reviewer (string, optional): Reviewer assigned to the project. + status (string, optional): Current project status inside the agency's workflow. domain (str): Overall domain to which the project belongs. today (date): Date of the day where the script is run. st (date): Project's start date, turned from an ISO-formatted string into a date. @@ -59,7 +62,7 @@ def __init__(self, title, client, source, target, words, start, deadline, price, TypeError: If 'price' is not a float. TypeError: If 'tm' is not a boolean. TypeError: If 'translator' is not a string. - TypeError: If 'revisor' is not a string. + TypeError: If 'reviewer' is not a string. TypeError: If 'status' is not a string. ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed. TypeError: If 'domain' is not a string. @@ -110,12 +113,12 @@ def __init__(self, title, client, source, target, words, start, deadline, price, self.translator = "internal" else: self.translator = translator - if type(revisor) != str: - raise TypeError("The revisor's name must be a string.") - elif revisor == '': - self.revisor = "internal" + if type(reviewer) != str: + raise TypeError("The reviewer's name must be a string.") + elif reviewer == '': + self.reviewer = "internal" else: - self.revisor = revisor + self.reviewer = reviewer if type(status) != str: raise TypeError("The status must be a string.") elif status == '': @@ -175,14 +178,14 @@ def project_length(self): def __str__(self): # defines the print behaviour: returns a text providing the main information about the project sent_1 = f"{self.title} is a translation for {self.client} from {self.source} into {self.target}." - if self.translator == "internal" and self.revisor == "internal": - sent_2 = f"Both the translator and the revisor are agency collaborators." - elif self.translator == "internal" and self.revisor != "internal": - sent_2 = f"The translator is an agency collaborator and the revisor is {self.revisor}." - elif self.translator != "internal" and self.revisor == "internal": - sent_2 = f"The translator is {self.translator} and the revisor is an agency collaborator." - else: - sent_2 = f"The translator is {self.translator} and the revisor is {self.revisor}." + if self.translator.lower() == "internal" and self.reviewer.lower() == "internal": + sent_2 = f"Both the translator and the reviewer are agency collaborators." + elif self.translator.lower() == "internal" and self.reviewer.lower() != "internal": + sent_2 = f"The translator is an agency collaborator and the reviewer is {self.reviewer}." + elif self.translator.lower() != "internal" and self.reviewer.lower() == "internal": + sent_2 = f"The translator is {self.translator} and the reviewer is an agency collaborator." + else: + sent_2 = f"The translator is {self.translator} and the reviewer is {self.reviewer}." # this if-statement considers whether a domain was added if len(self.domain) > 0: sent_3 = f"The domain is: {self.domain}." From c7157515e79aa9f73f3576919877d7a91181bc5b Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sat, 3 Jun 2023 17:50:36 +0200 Subject: [PATCH 24/46] Adding a Freelancer class, attempt 1 --- ...nt_trial-and-error_7_linking classes.ipynb | 669 ++++++++++++++++++ 1 file changed, 669 insertions(+) create mode 100644 Final-assignment_trial-and-error_7_linking classes.ipynb diff --git a/Final-assignment_trial-and-error_7_linking classes.ipynb b/Final-assignment_trial-and-error_7_linking classes.ipynb new file mode 100644 index 0000000..7839b2f --- /dev/null +++ b/Final-assignment_trial-and-error_7_linking classes.ipynb @@ -0,0 +1,669 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e8373e39", + "metadata": {}, + "source": [ + "# Import modules" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "b44e6905", + "metadata": {}, + "outputs": [], + "source": [ + "import datetime #datetime package to convert strings into dates, calculate time periods etc.\n", + "import re" + ] + }, + { + "cell_type": "markdown", + "id": "b73604ff", + "metadata": {}, + "source": [ + "# Freelancers" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "f5545676", + "metadata": {}, + "outputs": [], + "source": [ + "class Freelancers:\n", + " is_internal = False\n", + " \n", + " def __init__(self, name, email, phone, id):\n", + " self.name = name\n", + " self.email = email\n", + " self.phone = phone\n", + " self.id = id\n", + " \n", + " self.task = set()\n", + " self.language = set()\n", + " \n", + " def add_task(self, task):\n", + " self.task.add(task)\n", + " \n", + " def add_language(self, language):\n", + " self.language.add(language)\n", + " \n", + " def __str__(self):\n", + " sent_1 = f\"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone}).\"\n", + " sent_2 = f\"{self.name}'s ID in our database is {self.id}.\"\n", + " sent_3 = f\"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}.\"\n", + " return \"\\n\".join([sent_1, sent_2, sent_3])" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "559edff4", + "metadata": {}, + "outputs": [], + "source": [ + "sdw_info = {\n", + " 'name' : 'Sibylle de Woot',\n", + " 'email' : 'sdewoot@email.be',\n", + " 'phone' : '+32 485 12 34 56',\n", + " 'id' : 'sdw',\n", + " 'task' : [\n", + " 'translator',\n", + " 'reviewer'\n", + " ],\n", + " 'language' : [\n", + " 'FR',\n", + " 'NL',\n", + " 'EN',\n", + " 'DE'\n", + " ]\n", + "}\n", + "mm_info = {\n", + " 'name' : 'Mariana Montes',\n", + " 'email' : 'mariana.montes@company.com',\n", + " 'phone' : '+32 487 98 76 54',\n", + " 'id' : 'mm',\n", + " 'task' : [\n", + " 'reviewer'\n", + " ],\n", + " 'language' : [\n", + " 'ES',\n", + " 'EN'\n", + " ]\n", + "}\n", + "evdl_info = {\n", + " 'name' : 'Emily van der Londen',\n", + " 'email' : 'evdl@translation.net',\n", + " 'phone' : '+32 486 19 28 37',\n", + " 'id' : 'evdl',\n", + " 'task' : [\n", + " 'translator'\n", + " ],\n", + " 'language' : [\n", + " 'NL',\n", + " 'EN',\n", + " 'FR'\n", + " ]\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "a5d872cd", + "metadata": {}, + "outputs": [], + "source": [ + "freelancers = [sdw_info, mm_info, evdl_info]" + ] + }, + { + "cell_type": "markdown", + "id": "f8f569ba", + "metadata": {}, + "source": [ + "## Class instances from dictionary" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "15edcaa7", + "metadata": {}, + "outputs": [], + "source": [ + "class Freelancer():\n", + " def __init__(self, dictionary):\n", + "\n", + " for key, value in dictionary.items():\n", + " setattr(self, key, value)\n", + " \n", + " def __str__(self):\n", + " sent_1 = f\"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone}).\"\n", + " sent_2 = f\"{self.name}'s ID in our database is {self.id}.\"\n", + " sent_3 = f\"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}.\"\n", + " return \"\\n\".join([sent_1, sent_2, sent_3])\n", + "\n", + "sdw = Freelancer(sdw_info)\n", + "mm = Freelancer(mm_info)\n", + "evdl = Freelancer(evdl_info)" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "ae186e5a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Sibylle de Woot'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sdw.name" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "f22ef8fb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['translator', 'reviewer']" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sdw.task" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "08aa579e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sibylle de Woot can be contacted via e-mail (sdewoot@email.be) or via phone (+32 485 12 34 56).\n", + "Sibylle de Woot's ID in our database is sdw.\n", + "Sibylle de Woot works as a translator, reviewer for FR, NL, EN, DE.\n" + ] + } + ], + "source": [ + "print(sdw)" + ] + }, + { + "cell_type": "markdown", + "id": "1dccc238", + "metadata": {}, + "source": [ + "# Projects" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "ffae2adb", + "metadata": {}, + "outputs": [], + "source": [ + "class Project:\n", + "\n", + " def __init__(self, title, client, source, target, words, start, deadline, price, tm, translator = 'internal', reviewer = 'internal', status = 'created', domain = ''):\n", + " \"\"\"Initialises an object of the Project class, a class that represents a translation project of a translation agency.\n", + " \n", + " Args:\n", + " title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated).\n", + " client (str): Client who ordered the translation.\n", + " source (str): Language of the source document(s) (document(s) to be translated).\n", + " target (str): Language of the target document(s) (translation(s)).\n", + " words (int): Word count of the source document(s).\n", + " start (str): Project's start date in ISO format (YYYY-MM-DD).\n", + " deadline (str): Project's deadline in ISO format (YYYY-MM-DD).\n", + " price (float): Total price invoiced to the client (excl. VAT).\n", + " tm (bool): Whether a translation memory is available for this project.\n", + " translator (string, optional): Translator assigned to the project. Defaults to 'internal'.\n", + " reviewer (string, optional): Reviewer assigned to the project. Defaults to 'internal'.\n", + " status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'.\n", + " domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string.\n", + " \n", + " Attributes:\n", + " title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated).\n", + " client (str): Client who ordered the translation.\n", + " source (str): Language of the source document(s) (document(s) to be translated).\n", + " target (str): Language of the target document(s) (translation(s)).\n", + " words (int): Word count of the source document(s).\n", + " start (str): Project's start date in ISO format (YYYY-MM-DD).\n", + " deadline (str): Project's deadline in ISO format (YYYY-MM-DD).\n", + " price (float): Total price invoiced to the client (excl. VAT).\n", + " tm (bool): Whether a translation memory is available for this project.\n", + " translator (string, optional): Translator assigned to the project.\n", + " reviewer (string, optional): Reviewer assigned to the project.\n", + " status (string, optional): Current project status inside the agency's workflow.\n", + " domain (str): Overall domain to which the project belongs.\n", + " today (date): Date of the day where the script is run.\n", + " st (date): Project's start date, turned from an ISO-formatted string into a date.\n", + " dl (date): Project's deadline, turned from an ISO-formatted string into a date.\n", + " daysleft (timedelta): Time left until the project's deadline.\n", + " length (timedelta): Total time allocated to the project.\n", + " rate (float): Project's word rate.\n", + " efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek).\n", + " \n", + " Raises:\n", + " TypeError: If 'title' is not a string.\n", + " TypeError: If 'client' is not a string.\n", + " TypeError: If 'source' is not a string.\n", + " TypeError: If 'target' is not a string.\n", + " TypeError: If 'words' is not an integer.\n", + " TypeError: If 'start' is not a string.\n", + " ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits.\n", + " TypeError: If 'deadline' is not a string.\n", + " ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits.\n", + " TypeError: If 'price' is not a float.\n", + " TypeError: If 'tm' is not a boolean.\n", + " TypeError: If 'status' is not a string.\n", + " ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed.\n", + " TypeError: If 'domain' is not a string.\n", + " \"\"\"\n", + " if type(title) != str:\n", + " raise TypeError(\"The title must be a string.\")\n", + " else:\n", + " self.title = title\n", + " if type(client) != str:\n", + " raise TypeError(\"The client name must be a string.\")\n", + " else:\n", + " self.client = client\n", + " if type(source) != str:\n", + " raise TypeError(\"The source language must be a string.\")\n", + " else:\n", + " self.source = source\n", + " if type(target) != str:\n", + " raise TypeError(\"The target langague must be a string.\")\n", + " else:\n", + " self.target = target\n", + " if type(words) != int:\n", + " raise TypeError(\"The word count must be an integer.\")\n", + " else:\n", + " self.words = words\n", + " if type(start) != str:\n", + " raise TypeError(\"The start date must be provided as a string.\")\n", + " elif not re.match (\"[0-9]{4}-[0-9]{2}-[0-9]{2}\", start):\n", + " raise ValueError(\"A valid start date must be provided in ISO format.\")\n", + " else:\n", + " self.start = start\n", + " if type(deadline) != str:\n", + " raise TypeError(\"The deadline must be provided as a string.\")\n", + " elif not re.match (\"[0-9]{4}-[0-9]{2}-[0-9]{2}\", deadline):\n", + " raise ValueError(\"A valid deadline must be provided in ISO format.\")\n", + " else:\n", + " self.deadline = deadline\n", + " if type(price) != float:\n", + " raise TypeError(\"The price must be a float.\")\n", + " else:\n", + " self.price = price\n", + " if type(tm) != bool:\n", + " raise TypeError(\"The TM availability must be a boolean.\")\n", + " else:\n", + " self.tm = tm\n", + " if translator == '':\n", + " self.translator = \"internal\"\n", + " else:\n", + " self.translator = translator\n", + " if reviewer == '':\n", + " self.reviewer = \"internal\"\n", + " else:\n", + " self.reviewer = reviewer\n", + " if type(status) != str:\n", + " raise TypeError(\"The status must be a string.\")\n", + " elif status == '':\n", + " self.status = \"created\"\n", + " elif not status.lower() in [\"created\",\n", + " \"in translation\",\n", + " \"in revision\",\n", + " \"delivered\",\n", + " \"delayed\",\n", + " \"cancelled\",\n", + " \"canceled\"]:\n", + " raise ValueError(\"Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.\")\n", + " else:\n", + " self.status = status\n", + " if type(domain) != str:\n", + " raise TypeError(\"The domain must be a string.\")\n", + " else:\n", + " self.domain = domain\n", + " \n", + " today = datetime.date.today()\n", + " self.st = datetime.date.fromisoformat(start)\n", + " self.dl = datetime.date.fromisoformat(deadline)\n", + " self.daysleft = self.dl - today\n", + " self.length = self.dl - self.st\n", + " self.rate = self.price/self.words\n", + " self.efficiency = words/((5/7)*self.length.days)\n", + "\n", + " def days_left(self):\n", + " \"\"\"Displays how many days remain until the project deadline.\n", + " \n", + " Args:\n", + " None\n", + " \n", + " Returns:\n", + " str: A message informing the user that the deadline has been exceeded already if the deadline is in the past.\n", + " str: A message informing the user of the number of days left until the deadline if the deadline is not in the past.\n", + " \"\"\"\n", + " # prints a text indicating how many days are left until the project deadline\n", + " if self.dl < datetime.date.today():\n", + " # if the deadline is in the past\n", + " return f\"The deadline has been exceeded already.\"\n", + " else:\n", + " # if the deadline is not in the past\n", + " return f\"There are {self.daysleft.days} days left until the deadline.\"\n", + " \n", + " def project_length(self):\n", + " \"\"\"Displays the total number of days allocated to the project.\n", + " \n", + " Args:\n", + " None\n", + " \n", + " Returns:\n", + " str: The total number of days allocated to the project.\n", + " \"\"\"\n", + " return f\"{self.length.days} days\"\n", + " \n", + " def __str__(self):\n", + " # defines the print behaviour: returns a text providing the main information about the project\n", + " sent_1 = f\"{self.title} is a translation for {self.client} from {self.source} into {self.target}.\"\n", + " if self.translator == \"internal\" and self.reviewer == \"internal\":\n", + " sent_2 = f\"Both the translator and the reviewer are agency collaborators.\"\n", + " elif self.translator == \"internal\" and self.reviewer != \"internal\":\n", + " sent_2 = f\"The translator is an agency collaborator and the reviewer is {self.reviewer.name}.\"\n", + " elif self.translator != \"internal\" and self.reviewer == \"internal\":\n", + " sent_2 = f\"The translator is {self.translator.name} and the reviewer is an agency collaborator.\"\n", + " else:\n", + " sent_2 = f\"The translator is {self.translator.name} and the reviewer is {self.reviewer.name}.\"\n", + " # this if-statement considers whether a domain was added\n", + " if len(self.domain) > 0:\n", + " sent_3 = f\"The domain is: {self.domain}.\"\n", + " else:\n", + " sent_3 = \"The domain is unspecified.\" # if no domain was added, the text mentions it\n", + " sent_4 = f\"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word.\" #the word rate is rounded to two decimal places to avoid cumbersomely long numbers\n", + " # this if-statement considers whether the deadline is in the past\n", + " if self.dl < datetime.date.today():\n", + " sent_5 = f\"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day.\" # the efficiency is rounded to units because you can't translate a fraction of a word anyway\n", + " else:\n", + " sent_5 = f\"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day.\"\n", + " # this if-statement considers whether there is a translation memory for the project\n", + " sent_6 = f\"There is {'a' if self.tm else 'no'} translation memory.\"\n", + " sent_7 = f\"The project is currently {self.status}.\"\n", + " # print each sentence in a different line\n", + " return \"\\n\".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7])" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "af6d3dd5", + "metadata": {}, + "outputs": [], + "source": [ + "rhumatismes_inflammatoires = {\n", + " 'title' : 'La polyarthrite rhumatoïde et autres rhumatismes inflammatoires',\n", + " 'client' : 'Reuma vzw',\n", + " 'source' : 'FR',\n", + " 'target' : 'NL',\n", + " 'words' : 2131,\n", + " 'start' : '2020-09-24',\n", + " 'deadline' : '2020-10-15',\n", + " 'price' : 210.00,\n", + " 'tm' : False,\n", + " 'translator' : '',\n", + " 'reviewer' : '',\n", + " 'status' : '',\n", + " 'domain' : 'healthcare'\n", + "}\n", + "handboek = {\n", + " 'title' : 'Handboek voor studentenvertegenwoordigers',\n", + " 'client' : 'KU Leuven',\n", + " 'source' : 'NL',\n", + " 'target' : 'EN',\n", + " 'words' : 3654,\n", + " 'start' : '2023-02-21',\n", + " 'deadline' : '2023-03-02',\n", + " 'price' : 540.00,\n", + " 'tm' : True,\n", + " 'translator' : sdw,\n", + " 'reviewer' : '',\n", + " 'status' : 'delayed', \n", + " 'domain' : 'education'\n", + "}\n", + "user_guide = {\n", + " 'title' : 'User Guide MFPs',\n", + " 'client' : 'UGent',\n", + " 'source' : 'EN',\n", + " 'target' : 'NL',\n", + " 'words' : 1852,\n", + " 'start' : '2023-04-12',\n", + " 'deadline' : '2023-04-14',\n", + " 'price' : 280.00,\n", + " 'tm' : True,\n", + " 'translator' : '',\n", + " 'reviewer' : mm,\n", + " 'status' : 'cancelled',\n", + " 'domain' : ''\n", + "}\n", + "guide_bruxelles = {\n", + " 'title' : 'Guide de Bruxelles',\n", + " 'client' : 'Foodies',\n", + " 'source' : 'NL',\n", + " 'target' : 'FR',\n", + " 'words' : 11500,\n", + " 'start' : '2023-04-06',\n", + " 'deadline' : '2023-05-27',\n", + " 'price' : 1610.00,\n", + " 'tm' : False,\n", + " 'translator' : evdl,\n", + " 'reviewer' : sdw,\n", + " 'status' : 'in revision', \n", + " 'domain' : ''\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "9c855697", + "metadata": {}, + "outputs": [], + "source": [ + "translation_projects = [rhumatismes_inflammatoires, handboek, user_guide, guide_bruxelles]" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "23542b23", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "La polyarthrite rhumatoïde et autres rhumatismes inflammatoires is a translation for Reuma vzw from FR into NL.\n", + "Both the translator and the reviewer are agency collaborators.\n", + "The domain is: healthcare.\n", + "It's 2131 words long, with a rate of 0.1 € per word.\n", + "It started on 2020-09-24 and was due on 2020-10-15, so 21 days were foreseen for it. To meet the deadline, 142 words needed to be translated or revised per day.\n", + "There is no translation memory.\n", + "The project is currently created.\n", + "----\n", + "Handboek voor studentenvertegenwoordigers is a translation for KU Leuven from NL into EN.\n", + "The translator is Sibylle de Woot and the reviewer is an agency collaborator.\n", + "The domain is: education.\n", + "It's 3654 words long, with a rate of 0.15 € per word.\n", + "It started on 2023-02-21 and was due on 2023-03-02, so 9 days were foreseen for it. To meet the deadline, 568 words needed to be translated or revised per day.\n", + "There is a translation memory.\n", + "The project is currently delayed.\n", + "----\n", + "User Guide MFPs is a translation for UGent from EN into NL.\n", + "The translator is an agency collaborator and the reviewer is Mariana Montes.\n", + "The domain is unspecified.\n", + "It's 1852 words long, with a rate of 0.15 € per word.\n", + "It started on 2023-04-12 and was due on 2023-04-14, so 2 days were foreseen for it. To meet the deadline, 1296 words needed to be translated or revised per day.\n", + "There is a translation memory.\n", + "The project is currently cancelled.\n", + "----\n", + "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", + "The translator is Emily van der Londen and the reviewer is Sibylle de Woot.\n", + "The domain is unspecified.\n", + "It's 11500 words long, with a rate of 0.14 € per word.\n", + "It started on 2023-04-06 and was due on 2023-05-27, so 51 days were foreseen for it. To meet the deadline, 316 words needed to be translated or revised per day.\n", + "There is no translation memory.\n", + "The project is currently in revision.\n", + "----\n" + ] + } + ], + "source": [ + "for project in translation_projects:\n", + " my_project = Project(project['title'], project['client'], project['source'], project['target'], project['words'], project['start'], project['deadline'], project['price'], project['tm'], project['translator'], project['reviewer'], project['status'], project['domain'])\n", + " print(my_project)\n", + " print('----')" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "29975a1e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "1610.0" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_project.price" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "99078130", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "<__main__.Freelancer at 0x7fe637178a00>" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_project.reviewer" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "35e7b92c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Sibylle de Woot'" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_project.reviewer.name" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "0332a787", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'sdewoot@email.be'" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_project.reviewer.email" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1f569b3f", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From db81406d389e1cea475158fd6e9193116b6aa945 Mon Sep 17 00:00:00 2001 From: Sibylle Date: Sun, 11 Jun 2023 11:13:42 +0200 Subject: [PATCH 25/46] update argparse --- script-with-argparse.py | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/script-with-argparse.py b/script-with-argparse.py index 7e99e81..6221ebc 100644 --- a/script-with-argparse.py +++ b/script-with-argparse.py @@ -201,21 +201,13 @@ def __str__(self): return "\n".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7]) if __name__ == "__main": - parser = argparse.ArgumentParser("project") - parser.add_argument("title", type=str, help="Title of the project") - parser.add_argument("client", type=str, help="Client who ordered the translation") - parser.add_argument("source", type=str, help="Source language") - parser.add_argument("target", type=str, help="Target language") - parser.add_argument("words", type=int, help="Project word count") - parser.add_argument("start", type=str, help="Project start date") - parser.add_argument("deadline", type=str, help="Project deadline") - parser.add_argument("price", type=float, help="Total price project") - parser.add_argument("tm", type=bool, help="Whether a translation memory is available") - parser.add_argument("--translator", type=str, default = "internal", help="Translator assigned to the project") - parser.add_argument("--revisor", type=str, default = "internal",help="Revisor assigned to the project") - parser.add_argument("--status", type=str, default = "created", - choices = ["created", "in translation", "in revision", "delivered", "delayed", "cancelled", "canceled"], help="Project status in the agency workflow") - parser.add_argument("--domain", type=str, default = "", help="Overall domain of the text") + parser = argparse.ArgumentParser( + "project", + description = "Read a project list and print the project information.", + epilog = "You get an overview of the agency's projects.") + parser.add_argument("filename", + type=argparse.FileType("r", encoding="utf-8"), + help="The list of translation projects") args = parser.parse_args() proj = Project(args.title, args.client, args.source, args.target, args.words, args.start, args.deadline, args.price, args.tm, args.translator, args.revisor, args.status, args.domain) proj.days_left() From 18e955f0c9d7547ac3c28f20ed076f3c1fa5cc13 Mon Sep 17 00:00:00 2001 From: Sibylle Date: Sun, 11 Jun 2023 11:44:22 +0200 Subject: [PATCH 26/46] arguments separate lines --- script-with-argparse.py | 16 +++++++++++++++- script.py | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/script-with-argparse.py b/script-with-argparse.py index 6221ebc..6062c18 100644 --- a/script-with-argparse.py +++ b/script-with-argparse.py @@ -9,7 +9,21 @@ class Project: - def __init__(self, title, client, source, target, words, start, deadline, price, tm, translator = 'internal', revisor = 'internal', status = 'created', domain = ''): + def __init__( + self, + title, + client, + source, + target, + words, + start, + deadline, + price, + tm, + translator = 'internal', + revisor = 'internal', + status = 'created', + domain = ''): """Initialises an object of the Project class, a class that represents a translation project of a translation agency. Args: diff --git a/script.py b/script.py index 3d28a19..14ee552 100644 --- a/script.py +++ b/script.py @@ -9,7 +9,21 @@ class Project: - def __init__(self, title, client, source, target, words, start, deadline, price, tm, translator = 'internal', reviewer = 'internal', status = 'created', domain = ''): + def __init__( + self, + title, + client, + source, + target, + words, + start, + deadline, + price, + tm, + translator = 'internal', + reviewer = 'internal', + status = 'created', + domain = ''): """Initialises an object of the Project class, a class that represents a translation project of a translation agency. Args: From 7a46a5a30bb263354d3e2a942351d2f67204d963 Mon Sep 17 00:00:00 2001 From: Sibylle Date: Sun, 11 Jun 2023 11:48:19 +0200 Subject: [PATCH 27/46] print variables argparse --- script-with-argparse.py | 1 + 1 file changed, 1 insertion(+) diff --git a/script-with-argparse.py b/script-with-argparse.py index 6062c18..27d3898 100644 --- a/script-with-argparse.py +++ b/script-with-argparse.py @@ -226,4 +226,5 @@ def __str__(self): proj = Project(args.title, args.client, args.source, args.target, args.words, args.start, args.deadline, args.price, args.tm, args.translator, args.revisor, args.status, args.domain) proj.days_left() proj.project_length() + print(proj) \ No newline at end of file From 0fcf15e75de97d07f72421c82b66935a7ee753dc Mon Sep 17 00:00:00 2001 From: Sibylle Date: Sun, 11 Jun 2023 11:58:51 +0200 Subject: [PATCH 28/46] validation start and deadline --- script.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/script.py b/script.py index 14ee552..44dd874 100644 --- a/script.py +++ b/script.py @@ -103,14 +103,18 @@ def __init__( self.words = words if type(start) != str: raise TypeError("The start date must be provided as a string.") - elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", start): - raise ValueError("A valid start date must be provided in ISO format.") + try: + self.st = datetime.date.fromisoformat(start) + except: + raise TypeError("The start date must be provided in ISOFormat") else: self.start = start if type(deadline) != str: raise TypeError("The deadline must be provided as a string.") - elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", deadline): - raise ValueError("A valid deadline must be provided in ISO format.") + try: + self.dl = datetime.date.fromisoformat(deadline) + except: + raise TypeError("The start deadline must be provided in ISOFormat") else: self.deadline = deadline if type(price) != float: @@ -153,8 +157,6 @@ def __init__( self.domain = domain today = datetime.date.today() - self.st = datetime.date.fromisoformat(start) - self.dl = datetime.date.fromisoformat(deadline) self.daysleft = self.dl - today self.length = self.dl - self.st self.rate = self.price/self.words From 9fbb7dd043f850535606449818557bb5b460be93 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Mon, 12 Jun 2023 15:17:57 +0200 Subject: [PATCH 29/46] Linking classes, attempt 2 --- ...nt_trial-and-error_8_linking classes_v2.py | 534 ++++++++++++++++++ 1 file changed, 534 insertions(+) create mode 100644 Final-assignment_trial-and-error_8_linking classes_v2.py diff --git a/Final-assignment_trial-and-error_8_linking classes_v2.py b/Final-assignment_trial-and-error_8_linking classes_v2.py new file mode 100644 index 0000000..67aa618 --- /dev/null +++ b/Final-assignment_trial-and-error_8_linking classes_v2.py @@ -0,0 +1,534 @@ +#!/usr/bin/env python +# coding: utf-8 + +# # Import modules + +# In[1]: + + +import datetime #datetime package to convert strings into dates, calculate time periods etc. +import re +import json + + +# # Freelancers + +# In[12]: + + +class Freelancers: + is_internal = False + + def __init__(self, name, email, phone, ref): + self.name = name + self.email = email + self.phone = phone + self.ref = ref + + self.task = set() + self.language = set() + + def add_task(self, task): + self.task.add(task) + + def add_language(self, language): + self.language.add(language) + + def __str__(self): + sent_1 = f"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone})." + sent_2 = f"{self.name}'s ID in our database is {self.ref}." + sent_3 = f"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}." + return "\n".join([sent_1, sent_2, sent_3]) + + +# In[2]: + + +sdw = { + 'name' : 'Sibylle de Woot', + 'email' : 'sdewoot@email.be', + 'phone' : '+32 485 12 34 56', + 'ref' : 'sdw', + 'task' : [ + 'translator', + 'reviewer' + ], + 'language' : [ + 'FR', + 'NL', + 'EN', + 'DE' + ] +} +mm = { + 'name' : 'Mariana Montes', + 'email' : 'mariana.montes@company.com', + 'phone' : '+32 487 98 76 54', + 'ref' : 'mm', + 'task' : [ + 'reviewer' + ], + 'language' : [ + 'ES', + 'EN' + ] +} +evdl = { + 'name' : 'Emily van der Londen', + 'email' : 'evdl@translation.net', + 'phone' : '+32 486 19 28 37', + 'ref' : 'evdl', + 'task' : [ + 'translator' + ], + 'language' : [ + 'NL', + 'EN', + 'FR' + ] +} + + +# In[3]: + + +freelancers = [sdw, mm, evdl] + + +# In[6]: + + +with open('freelancers_db.json', 'w', encoding='utf-8') as f: + json.dump(freelancers, f) + + +# ## Class instances from dictionary + +# In[3]: + + +class Freelancer(): + def __init__(self, dictionary): + + for key, value in dictionary.items(): + setattr(self, key, value) + + def __str__(self): + sent_1 = f"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone})." + sent_2 = f"{self.name}'s ID in our database is {self.id}." + sent_3 = f"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}." + return "\n".join([sent_1, sent_2, sent_3]) + +sdw = Freelancer(sdw_info) +mm = Freelancer(mm_info) +evdl = Freelancer(evdl_info) + + +# In[20]: + + +sdw.name + + +# In[21]: + + +sdw.task + + +# In[25]: + + +print(sdw) + + +# # Class instances with kwargs + +# In[2]: + + +class Freelancer(): + + def __init__(self, name, email, phone, ref): + self.name = name + self.email = email + self.phone = phone + self.ref = ref + + self.task = set() + self.language = set() + + def add_task(self, task): + self.task.add(task) + + def add_language(self, language): + self.language.add(language) + + def function_with_kwargs(**kwargs): + if "name" in kwargs: + print(kwargs["name"]) + else: + print("no name found") + + def __str__(self): + sent_1 = f"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone})." + sent_2 = f"{self.name}'s ID in our database is {self.id}." + sent_3 = f"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}." + return "\n".join([sent_1, sent_2, sent_3]) + + +# # Projects + +# In[3]: + + +class Project: + + def __init__(self, title, client, source, target, words, start, deadline, price, tm, translator = 'internal', reviewer = 'internal', status = 'created', domain = ''): + """Initialises an object of the Project class, a class that represents a translation project of a translation agency. + + Args: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + translator (string, optional): Translator assigned to the project. Defaults to 'internal'. + reviewer (string, optional): Reviewer assigned to the project. Defaults to 'internal'. + status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'. + domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string. + + Attributes: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + translator (string, optional): Translator assigned to the project. + reviewer (string, optional): Reviewer assigned to the project. + status (string, optional): Current project status inside the agency's workflow. + domain (str): Overall domain to which the project belongs. + today (date): Date of the day where the script is run. + st (date): Project's start date, turned from an ISO-formatted string into a date. + dl (date): Project's deadline, turned from an ISO-formatted string into a date. + daysleft (timedelta): Time left until the project's deadline. + length (timedelta): Total time allocated to the project. + rate (float): Project's word rate. + efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek). + + Raises: + TypeError: If 'title' is not a string. + TypeError: If 'client' is not a string. + TypeError: If 'source' is not a string. + TypeError: If 'target' is not a string. + TypeError: If 'words' is not an integer. + TypeError: If 'start' is not a string. + ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'deadline' is not a string. + ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'price' is not a float. + TypeError: If 'tm' is not a boolean. + TypeError: If 'status' is not a string. + ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed. + TypeError: If 'domain' is not a string. + """ + if type(title) != str: + raise TypeError("The title must be a string.") + else: + self.title = title + if type(client) != str: + raise TypeError("The client name must be a string.") + else: + self.client = client + if type(source) != str: + raise TypeError("The source language must be a string.") + else: + self.source = source + if type(target) != str: + raise TypeError("The target langague must be a string.") + else: + self.target = target + if type(words) != int: + raise TypeError("The word count must be an integer.") + else: + self.words = words + if type(start) != str: + raise TypeError("The start date must be provided as a string.") + elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", start): + raise ValueError("A valid start date must be provided in ISO format.") + else: + self.start = start + if type(deadline) != str: + raise TypeError("The deadline must be provided as a string.") + elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", deadline): + raise ValueError("A valid deadline must be provided in ISO format.") + else: + self.deadline = deadline + if type(price) != float: + raise TypeError("The price must be a float.") + else: + self.price = price + if type(tm) != bool: + raise TypeError("The TM availability must be a boolean.") + else: + self.tm = tm + if type(translator) != str and type(translator) != Freelancer: + raise TypeError("The translator's name must be a string or an entry in our freelancer database.") + elif translator == '': + self.translator = "internal" + else: + self.translator = translator + if type(reviewer) != str and type(reviewer) != Freelancer: + raise TypeError("The reviewer's name must be a string or an entry in our freelancer database.") + elif reviewer == '': + self.reviewer = "internal" + else: + self.reviewer = reviewer + if type(status) != str: + raise TypeError("The status must be a string.") + elif status == '': + self.status = "created" + elif not status.lower() in ["created", + "in translation", + "in revision", + "delivered", + "delayed", + "cancelled", + "canceled"]: + raise ValueError("Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.") + else: + self.status = status + if type(domain) != str: + raise TypeError("The domain must be a string.") + else: + self.domain = domain + + today = datetime.date.today() + self.st = datetime.date.fromisoformat(start) + self.dl = datetime.date.fromisoformat(deadline) + self.daysleft = self.dl - today + self.length = self.dl - self.st + self.rate = self.price/self.words + self.efficiency = words/((5/7)*self.length.days) + + def days_left(self): + """Displays how many days remain until the project deadline. + + Args: + None + + Returns: + str: A message informing the user that the deadline has been exceeded already if the deadline is in the past. + str: A message informing the user of the number of days left until the deadline if the deadline is not in the past. + """ + # prints a text indicating how many days are left until the project deadline + if self.dl < datetime.date.today(): + # if the deadline is in the past + return f"The deadline has been exceeded already." + else: + # if the deadline is not in the past + return f"There are {self.daysleft.days} days left until the deadline." + + def project_length(self): + """Displays the total number of days allocated to the project. + + Args: + None + + Returns: + str: The total number of days allocated to the project. + """ + return f"{self.length.days} days" + + def __str__(self): + # defines the print behaviour: returns a text providing the main information about the project + sent_1 = f"{self.title} is a translation for {self.client} from {self.source} into {self.target}." + if self.translator == "internal" and self.reviewer == "internal": + sent_2 = f"Both the translator and the reviewer are agency collaborators." + elif self.translator == "internal" and self.reviewer != "internal": + sent_2 = f"The translator is an agency collaborator and the reviewer is {self.reviewer.name}." + elif self.translator != "internal" and self.reviewer == "internal": + sent_2 = f"The translator is {self.translator.name} and the reviewer is an agency collaborator." + else: + sent_2 = f"The translator is {self.translator.name} and the reviewer is {self.reviewer.name}." + # this if-statement considers whether a domain was added + if len(self.domain) > 0: + sent_3 = f"The domain is: {self.domain}." + else: + sent_3 = "The domain is unspecified." # if no domain was added, the text mentions it + sent_4 = f"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word." #the word rate is rounded to two decimal places to avoid cumbersomely long numbers + # this if-statement considers whether the deadline is in the past + if self.dl < datetime.date.today(): + sent_5 = f"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day." # the efficiency is rounded to units because you can't translate a fraction of a word anyway + else: + sent_5 = f"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day." + # this if-statement considers whether there is a translation memory for the project + sent_6 = f"There is {'a' if self.tm else 'no'} translation memory." + sent_7 = f"The project is currently {self.status}." + # print each sentence in a different line + return "\n".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7]) + + +# In[4]: + + +rhumatismes_inflammatoires = { + 'title' : 'La polyarthrite rhumatoïde et autres rhumatismes inflammatoires', + 'client' : 'Reuma vzw', + 'source' : 'FR', + 'target' : 'NL', + 'words' : 2131, + 'start' : '2020-09-24', + 'deadline' : '2020-10-15', + 'price' : 210.00, + 'tm' : False, + 'translator' : '', + 'reviewer' : '', + 'status' : '', + 'domain' : 'healthcare' +} +handboek = { + 'title' : 'Handboek voor studentenvertegenwoordigers', + 'client' : 'KU Leuven', + 'source' : 'NL', + 'target' : 'EN', + 'words' : 3654, + 'start' : '2023-02-21', + 'deadline' : '2023-03-02', + 'price' : 540.00, + 'tm' : True, + 'translator' : 'sdw', + 'reviewer' : '', + 'status' : 'delayed', + 'domain' : 'education' +} +user_guide = { + 'title' : 'User Guide MFPs', + 'client' : 'UGent', + 'source' : 'EN', + 'target' : 'NL', + 'words' : 1852, + 'start' : '2023-04-12', + 'deadline' : '2023-04-14', + 'price' : 280.00, + 'tm' : True, + 'translator' : '', + 'reviewer' : 'mm', + 'status' : 'cancelled', + 'domain' : '' +} +guide_bruxelles = { + 'title' : 'Guide de Bruxelles', + 'client' : 'Foodies', + 'source' : 'NL', + 'target' : 'FR', + 'words' : 11500, + 'start' : '2023-04-06', + 'deadline' : '2023-05-27', + 'price' : 1610.00, + 'tm' : False, + 'translator' : 'evdl', + 'reviewer' : 'sdw', + 'status' : 'in revision', + 'domain' : '' +} + + +# In[6]: + + +translation_projects = [rhumatismes_inflammatoires, handboek, user_guide, guide_bruxelles] + + +# In[16]: + + +for project in translation_projects: + my_project = Project(project['title'], project['client'], project['source'], project['target'], project['words'], project['start'], project['deadline'], project['price'], project['tm'], project['translator'], project['reviewer'], project['status'], project['domain']) + print(my_project) + print('----') + + +# In[21]: + + +my_project.price + + +# In[22]: + + +my_project.reviewer + + +# In[23]: + + +my_project.reviewer.name + + +# In[25]: + + +my_project.reviewer.email + + +# # Separate jsons projects + +# In[5]: + + +with open('proj_rhumatismes_inflammatoires.json', 'w', encoding='utf-8') as f: + json.dump(rhumatismes_inflammatoires, f) + + +# In[6]: + + +with open('proj_handboek.json', 'w', encoding='utf-8') as f: + json.dump(handboek, f) + + +# In[7]: + + +with open('proj_user_guide.json', 'w', encoding='utf-8') as f: + json.dump(user_guide, f) + + +# In[8]: + + +with open('proj_guide_bruxelles.json', 'w', encoding='utf-8') as f: + json.dump(guide_bruxelles, f) + + +# # Linking projects and freelancers + +# In[9]: + + +with open('freelancers_db.json', 'r') as f: + my_freelancers = {x.ref : x for x in json.load(f)} # if the json is a list of dictionaries +with open('proj_guide_bruxelles.json', 'r') as f2: + project_dict = json.load(f2) + if 'translator' in project_dict and project_dict['translator'] in my_freelancers: + project_dict['translator'] = Freelancer(**my_freelancers[project_dict['translator']]) + if 'reviewer' in project_dict and project_dict['reviewer'] in my_freelancers: + project_dict['reviewer'] = Freelancer(**my_freelancers[project_dict['reviewer']]) + project = Project(**project_dict) # of course you have to allow + + +# In[ ]: + + + + From a0331e9e21f8ee75010a9b1c295f62abb8d18fe9 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:54:34 +0200 Subject: [PATCH 30/46] Deleting presentation files --- Demo-notebook.ipynb | 802 -------------------------------------------- 1 file changed, 802 deletions(-) delete mode 100644 Demo-notebook.ipynb diff --git a/Demo-notebook.ipynb b/Demo-notebook.ipynb deleted file mode 100644 index cfac671..0000000 --- a/Demo-notebook.ipynb +++ /dev/null @@ -1,802 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "b9f1fa80", - "metadata": {}, - "source": [ - "# Translation project manager for translation agency" - ] - }, - { - "cell_type": "markdown", - "id": "f0b84575", - "metadata": {}, - "source": [ - "## Starting point: Translation project manager for freelancer\n", - "\n", - "This script consisted of a single class `Translation` with the following attributes:\n", - "- A class attribute `translator`, which defaults to the freelancer's name.\n", - "- 10 attributes provided at initialisation:\n", - " - `title`(a string) indicates the project's title (typically the title of the source document, or the overall title the translator gave the project if there's more than one document to be translated);\n", - " - `client` (a string) indicates the client who ordered the translation;\n", - " - `source` (a string) indicates the language of the source document (document to be translated);\n", - " - `target` (a string) indicates the language of the target document (translation);\n", - " - `words` (an integer) indicates the word count of the source document;\n", - " - `start`(a string) indicates the project's start date in ISO format (YYYY-MM-DD);\n", - " - `deadline`(a string) indicates the project's deadline in ISO format (YYYY-MM-DD);\n", - " - `price` (an integer) indicates the total price invoiced to the client (excl. VAT);\n", - " - `tm` (a boolean) indicates whether or not a translation memory is available for this project;\n", - " - `domain` (a string) indicates the overall domain to which the project belongs.\n", - "- 4 computed attributes:\n", - " - `daysleft`, which calculates the number of days left until the project deadline by subtracting the current date from the deadline;\n", - " - `length`, which calculates the total number of days allotted for the project by subtracting the start date from the deadline;\n", - " - `rate`, which calculates the word rate for the project by dividing the total price by the word count in the source document;\n", - " - `efficiency`, which calculates how many words the translator needs to translate per day to meet the deadline." - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "b11c57bc", - "metadata": {}, - "outputs": [], - "source": [ - "import datetime #datetime package to convert strings into dates, calculate time periods etc." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "1f6d67d3", - "metadata": {}, - "outputs": [], - "source": [ - "class Translation:\n", - " translator = \"Sibylle\" # class attribute\n", - "\n", - " def __init__(self, title, client, source, target, words, start, deadline, price, tm, domain = ''):\n", - " # 'self' represents the object (= class element) itself\n", - " self.title = title\n", - " self.client = client\n", - " self.source = source\n", - " self.target = target\n", - " self.words = words\n", - " self.start = datetime.date.fromisoformat(start) # turns string into date\n", - " self.deadline = datetime.date.fromisoformat(deadline) # turns string into date\n", - " self.price = price\n", - " self.tm = tm\n", - " self.domain = domain\n", - " \n", - " today = datetime.date.today() # current date\n", - " self.daysleft = self.deadline - today # difference between deadline and current date\n", - " self.length = self.deadline - self.start # difference between deadline and start date\n", - " self.rate = self.price/self.words # word rate (price divided by word count)\n", - " self.efficiency = words/self.length.days # words to translate per day (word count divided by project length, see 'length' in explanations above)\n", - "\n", - " def days_left(self):\n", - " # prints a text indicating how many days are left until the project deadline\n", - " if self.deadline < datetime.date.today():\n", - " # if the deadline is in the past\n", - " return f\"The deadline has been exceeded already.\"\n", - " else:\n", - " # if the deadline is not in the past\n", - " return f\"There are {self.daysleft.days} days left until the deadline.\"\n", - " \n", - " def __str__(self):\n", - " # defines the print behaviour: returns a text providing the main information about the project\n", - " sent_1 = f\"{self.title} is a translation for {self.client} from {self.source} into {self.target}.\"\n", - " # this if-statement considers whether a domain was added\n", - " if len(self.domain) > 0:\n", - " sent_2 = f\"The domain is: {self.domain}.\"\n", - " else:\n", - " sent_2 = \"The domain is unspecified.\" # if no domain was added, the text mentions it\n", - " sent_3 = f\"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word.\" #the word rate is rounded to two decimal places to avoid cumbersomely long numbers\n", - " # this if-statement considers whether the deadline is in the past\n", - " if self.deadline < datetime.date.today():\n", - " sent_4 = f\"It started on {self.start} and was due on {self.deadline}, so I had {self.length.days} days for it. I needed to translate {round(self.efficiency,0)} words per day to meet the deadline.\" # the efficiency is rounded to units because you can't translate a fraction of a word anyway\n", - " else:\n", - " sent_4 = f\"It started on {self.start} and is due on {self.deadline}, so I have {self.length.days} days for it, of which {self.daysleft.days} left. I need to translate {round(self.efficiency,0)} words per day to meet the deadline.\"\n", - " # this if-statement considers whether there is a translation memory for the project\n", - " if self.tm is True:\n", - " sent_5 = f\"There is a translation memory.\"\n", - " else:\n", - " sent_5 = f\"There is no translation memory\"\n", - " # print each sentence in a different line\n", - " return \"\\n\".join([sent_1, sent_2, sent_3, sent_4, sent_5])" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "e92b9a05", - "metadata": {}, - "outputs": [], - "source": [ - "test1 = Translation('Guide de Bruxelles', 'Foodies', 'NL', 'FR', 11500, '2023-03-22', '2023-05-06', 1610, False)" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "ac4d553d", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", - "The domain is unspecified.\n", - "It's 11500 words long, with a rate of 0.14 € per word.\n", - "It started on 2023-03-22 and was due on 2023-05-06, so I had 45 days for it. I needed to translate 256.0 words per day to meet the deadline.\n", - "There is no translation memory\n" - ] - } - ], - "source": [ - "print(test1)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "c4ec3c60", - "metadata": {}, - "outputs": [], - "source": [ - "import json" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "9517724c", - "metadata": {}, - "outputs": [], - "source": [ - "translations_file = 'translation_projects.json' # assign filename to a string variable\n", - "with open(translations_file, encoding = 'utf-8') as f:\n", - " # open file and use json to parse it\n", - " translations = json.load(f) # translations is now a list of dictionaries. " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "d52e308a", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "La polyarthrite rhumatoïde et autres rhumatismes inflammatoires is a translation for Reuma vzw from FR into NL.\n", - "The domain is: healthcare.\n", - "It's 2131 words long, with a rate of 0.1 € per word.\n", - "It started on 2020-09-24 and was due on 2020-10-15, so I had 21 days for it. I needed to translate 101.0 words per day to meet the deadline.\n", - "There is no translation memory\n", - "----\n", - "Handboek voor studentenvertegenwoordigers is a translation for KU Leuven from NL into EN.\n", - "The domain is: education.\n", - "It's 3654 words long, with a rate of 0.15 € per word.\n", - "It started on 2023-02-21 and was due on 2023-03-02, so I had 9 days for it. I needed to translate 406.0 words per day to meet the deadline.\n", - "There is a translation memory.\n", - "----\n", - "User Guide MFPs is a translation for UGent from EN into NL.\n", - "The domain is unspecified.\n", - "It's 1852 words long, with a rate of 0.15 € per word.\n", - "It started on 2023-04-14 and was due on 2023-04-16, so I had 2 days for it. I needed to translate 926.0 words per day to meet the deadline.\n", - "There is a translation memory.\n", - "----\n", - "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", - "The domain is unspecified.\n", - "It's 11500 words long, with a rate of 0.14 € per word.\n", - "It started on 2023-03-22 and was due on 2023-05-06, so I had 45 days for it. I needed to translate 256.0 words per day to meet the deadline.\n", - "There is no translation memory\n", - "----\n" - ] - } - ], - "source": [ - "# go through each of the items in the list\n", - "for translation in translations:\n", - " # create a Translation instance with title, client, source, target, words, start, deadline, price, tm and domain\n", - " my_translation = Translation(translation['title'], translation['client'], translation['source'], translation['target'], translation['words'], translation['start'], translation['deadline'], translation['price'], translation['tm'], translation['domain'])\n", - " \n", - " # print the project information\n", - " print(my_translation)\n", - " \n", - " # print a separating line between translations\n", - " print('----')" - ] - }, - { - "cell_type": "markdown", - "id": "4fbcdaa6", - "metadata": {}, - "source": [ - "## Expanding and improving the script for a translation agency\n", - "- Actually putting the translator's identity in the printed project information (since the database now contains projects handled by various translators).\n", - "- Adding a `revisor` and `status` attribute.\n", - "- No longer applying the conversion to ISO format of start date and deadline at the instance attribute level, but creating extra computed attributes `self.st` and `self.dl` used only in calculations (makes calling the `start` and `deadline` attributes more user-friendly)." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "id": "728a7f92", - "metadata": {}, - "outputs": [], - "source": [ - "class Translation_agency:\n", - " translator = \"Internal\" # class attribute\n", - " revisor = \"Internal\"\n", - " status = \"created\"\n", - "\n", - " def __init__(self, title, client, source, target, words, start, deadline, price, tm, domain = ''):\n", - " # 'self' represents the object (= class element) itself\n", - " self.title = title\n", - " self.client = client\n", - " self.source = source\n", - " self.target = target\n", - " self.words = words\n", - " self.start = start\n", - " self.deadline = deadline\n", - " self.price = price\n", - " self.tm = tm\n", - " self.domain = domain\n", - " \n", - " today = datetime.date.today() # current date\n", - " self.st = datetime.date.fromisoformat(start) # turns string into date\n", - " self.dl = datetime.date.fromisoformat(deadline) # turns string into date\n", - " self.daysleft = self.dl - today # difference between deadline and current date\n", - " self.length = self.dl - self.st # difference between deadline and start date\n", - " self.rate = self.price/self.words # word rate (price divided by word count)\n", - " self.efficiency = words/self.length.days # words to translate per day (word count divided by project length, see 'length' in explanations above)\n", - "\n", - " def days_left(self):\n", - " # prints a text indicating how many days are left until the project deadline\n", - " if self.dl < datetime.date.today():\n", - " # if the deadline is in the past\n", - " return f\"The deadline has been exceeded already.\"\n", - " else:\n", - " # if the deadline is not in the past\n", - " return f\"There are {self.daysleft.days} days left until the deadline.\"\n", - " \n", - " def project_length(self):\n", - " return f\"{self.length.days} days\"\n", - " \n", - " def __str__(self):\n", - " # defines the print behaviour: returns a text providing the main information about the project\n", - " sent_1 = f\"{self.title} is a translation for {self.client} from {self.source} into {self.target}.\"\n", - " if self.translator == \"Internal\" and self.revisor == \"Internal\":\n", - " sent_2 = f\"Both the translator and the revisor are agency collaborators.\"\n", - " elif self.translator == \"Internal\" and self.revisor != \"Internal\":\n", - " sent_2 = f\"The translator is an agency collaborator and the revisor is {self.revisor}.\"\n", - " elif self.translator != \"Internal\" and self.revisor == \"Internal\":\n", - " sent_2 = f\"The translator is {self.translator} and the revisor is an agency collaborator.\"\n", - " else:\n", - " sent_2 = f\"The translator is {self.translator} and the revisor is {self.revisor}.\"\n", - " # this if-statement considers whether a domain was added\n", - " if len(self.domain) > 0:\n", - " sent_3 = f\"The domain is: {self.domain}.\"\n", - " else:\n", - " sent_3 = \"The domain is unspecified.\" # if no domain was added, the text mentions it\n", - " sent_4 = f\"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word.\" #the word rate is rounded to two decimal places to avoid cumbersomely long numbers\n", - " # this if-statement considers whether the deadline is in the past\n", - " if self.dl < datetime.date.today():\n", - " sent_5 = f\"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day.\" # the efficiency is rounded to units because you can't translate a fraction of a word anyway\n", - " else:\n", - " sent_5 = f\"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day.\"\n", - " # this if-statement considers whether there is a translation memory for the project\n", - " sent_6 = f\"There is {'a' if self.tm else 'no'} translation memory.\"\n", - " sent_7 = f\"The project is currently {self.status}.\"\n", - " # print each sentence in a different line\n", - " return \"\\n\".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7])" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "id": "37cd848e", - "metadata": {}, - "outputs": [], - "source": [ - "rhumatismes_inflammatoires = {\n", - " 'translator' : 'Internal',\n", - " 'revisor' : 'Internal',\n", - " 'status' : 'delivered',\n", - " 'title' : 'La polyarthrite rhumatoïde et autres rhumatismes inflammatoires',\n", - " 'client' : 'Reuma vzw',\n", - " 'source' : 'FR',\n", - " 'target' : 'NL',\n", - " 'words' : 2131,\n", - " 'start' : '2020-09-24',\n", - " 'deadline' : '2020-10-15',\n", - " 'price' : 210,\n", - " 'tm' : False,\n", - " 'domain' : 'healthcare'\n", - "}\n", - "handboek = {\n", - " 'translator' : 'Sibylle',\n", - " 'revisor' : 'Internal',\n", - " 'status' : 'delayed',\n", - " 'title' : 'Handboek voor studentenvertegenwoordigers',\n", - " 'client' : 'KU Leuven',\n", - " 'source' : 'NL',\n", - " 'target' : 'EN',\n", - " 'words' : 3654,\n", - " 'start' : '2023-02-21',\n", - " 'deadline' : '2023-03-02',\n", - " 'price' : 540,\n", - " 'tm' : True,\n", - " 'domain' : 'education'\n", - "}\n", - "user_guide = {\n", - " 'translator' : 'Internal',\n", - " 'revisor' : 'Sibylle',\n", - " 'status' : 'cancelled',\n", - " 'title' : 'User Guide MFPs',\n", - " 'client' : 'UGent',\n", - " 'source' : 'EN',\n", - " 'target' : 'NL',\n", - " 'words' : 1852,\n", - " 'start' : '2023-04-12',\n", - " 'deadline' : '2023-04-14',\n", - " 'price' : 280,\n", - " 'tm' : True,\n", - " 'domain' : ''\n", - "}\n", - "guide_bruxelles = {\n", - " 'translator' : 'Sibylle',\n", - " 'revisor' : 'Natacha',\n", - " 'status' : 'in revision',\n", - " 'title' : 'Guide de Bruxelles',\n", - " 'client' : 'Foodies',\n", - " 'source' : 'NL',\n", - " 'target' : 'FR',\n", - " 'words' : 11500,\n", - " 'start' : '2023-04-06',\n", - " 'deadline' : '2023-05-27',\n", - " 'price' : 1610,\n", - " 'tm' : False,\n", - " 'domain' : ''\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "83aaa188", - "metadata": {}, - "outputs": [], - "source": [ - "translation_projects = [rhumatismes_inflammatoires, handboek, user_guide, guide_bruxelles]" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "d17cc1a8", - "metadata": {}, - "outputs": [], - "source": [ - "with open('translation_agency_projects.json', 'w', encoding='utf-8') as f:\n", - " json.dump(translation_projects, f)" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "3e65cbb7", - "metadata": {}, - "outputs": [], - "source": [ - "translation_agency_file = 'translation_agency_projects.json' # assign filename to a string variable\n", - "with open(translation_agency_file, encoding = 'utf-8') as f:\n", - " # open file and use json to parse it\n", - " translations_agency = json.load(f) # translations is now a list of dictionaries. " - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "eb0cc334", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "La polyarthrite rhumatoïde et autres rhumatismes inflammatoires is a translation for Reuma vzw from FR into NL.\n", - "Both the translator and the revisor are agency collaborators.\n", - "The domain is: healthcare.\n", - "It's 2131 words long, with a rate of 0.1 € per word.\n", - "It started on 2020-09-24 and was due on 2020-10-15, so 21 days were foreseen for it. To meet the deadline, 101 words needed to be translated or revised per day.\n", - "There is no translation memory.\n", - "The project is currently delivered.\n", - "----\n", - "Handboek voor studentenvertegenwoordigers is a translation for KU Leuven from NL into EN.\n", - "The translator is Sibylle and the revisor is an agency collaborator.\n", - "The domain is: education.\n", - "It's 3654 words long, with a rate of 0.15 € per word.\n", - "It started on 2023-02-21 and was due on 2023-03-02, so 9 days were foreseen for it. To meet the deadline, 406 words needed to be translated or revised per day.\n", - "There is a translation memory.\n", - "The project is currently delayed.\n", - "----\n", - "User Guide MFPs is a translation for UGent from EN into NL.\n", - "The translator is an agency collaborator and the revisor is Sibylle.\n", - "The domain is unspecified.\n", - "It's 1852 words long, with a rate of 0.15 € per word.\n", - "It started on 2023-04-12 and was due on 2023-04-14, so 2 days were foreseen for it. To meet the deadline, 926 words needed to be translated or revised per day.\n", - "There is a translation memory.\n", - "The project is currently cancelled.\n", - "----\n", - "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", - "The translator is Sibylle and the revisor is Natacha.\n", - "The domain is unspecified.\n", - "It's 11500 words long, with a rate of 0.14 € per word.\n", - "It started on 2023-04-06 and is due on 2023-05-27, so 51 days are foreseen for it, of which 5 left. To meet the deadline, 225 words need to be translated or revised per day.\n", - "There is no translation memory.\n", - "The project is currently in revision.\n", - "----\n" - ] - } - ], - "source": [ - "# go through each of the items in the list\n", - "for translation in translations_agency:\n", - " # create a Translation instance with title, client, source, target, words, start, deadline, price, tm and domain\n", - " my_translation2 = Translation_agency(translation['title'], translation['client'], translation['source'], translation['target'], translation['words'], translation['start'], translation['deadline'], translation['price'], translation['tm'], translation['domain'])\n", - " if translation['translator'] != \"Internal\":\n", - " my_translation2.translator = translation['translator']\n", - " if translation['revisor'] != \"Internal\":\n", - " my_translation2.revisor = translation['revisor']\n", - " if translation['status'] != \"created\":\n", - " my_translation2.status = translation['status'] \n", - " # print the project information\n", - " print(my_translation2)\n", - " \n", - " # print a separating line between translations\n", - " print('----')" - ] - }, - { - "cell_type": "markdown", - "id": "bb384876", - "metadata": {}, - "source": [ - "## Left to do\n", - "- Add validation when updating the status: only allow 'created', 'in translation', 'in revision', 'delivered', 'delayed' and 'cancel(l)ed'.\n", - "- Add validation for all the other attributes (not accept 'internal' for translator and revisor, only 'Internal').\n", - "- Create a second class `Freelancers` fed by a json-file containing a freelancer database and use references to the database rather than strings (names) for the name of external translators and revisors (__how ?__).\n", - " - Last name\n", - " - First name\n", - " - Phone number\n", - " - E-mail address\n", - " - Project count\n", - "- Use regex to check e-mail address and phone number in the freelancer database.\n", - "- Implement argparse.\n", - "- Document the script with docstrings." - ] - }, - { - "cell_type": "markdown", - "id": "531f42ed", - "metadata": {}, - "source": [ - "# Extra: Source and target text aligner\n", - "Sometimes, you still have some translations left over from a time where you didn't use CAT-tools and you'd like to feed them into your translation memory. Some CAT-tools have built-in text aligners, but not all of them, so how do you go from two separate text documents to an aligned bilingual (csv-)file ready to be fed into your TM?\n", - "\n", - "## Step one: Prepare the source and target text\n", - "The easiest file format to start from is a pure txt-file... and since for a TM only the pure text is of interest, converting a Word-, PowerPoint- or whatever file to a txt-file isn't an issue. So, we'll take the original source and target document and export them to a txt-format (with utf-8 encoding).\n", - "\n", - "## Step two: Store the two (continuous) texts into variables" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "4150607b", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Introduction to Machine Learning with Python.\\n\\nThis module provides an introduction to the basic concepts and use of the Python programming language in support of translation. Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.\\n'" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Source text\n", - "f = open('python_en.txt', encoding = 'utf-8')\n", - "st_1 = f.read()\n", - "f.close()\n", - "st_1" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "ee31b753", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Introduction au machine learning à l’aide de Python.\\n\\nCe module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.\\n'" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Source text\n", - "f = open('python_fr.txt', encoding = 'utf-8')\n", - "tt_1 = f.read()\n", - "f.close()\n", - "tt_1" - ] - }, - { - "cell_type": "markdown", - "id": "bb6de39e", - "metadata": {}, - "source": [ - "## Step three: Split the single text string into list of sentences\n", - "Since most TMs (and CAT-tools) use sentence segmentation, the source and target text need to be split up into sentences. So, each text becomes a list of separate sentences.\n", - "\n", - "For this, we use `nltk tokenizer`, which functions with English and French (and many other languages, but English and French are the ones that interest us right now)." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "b6d77857", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['Introduction to Machine Learning with Python.',\n", - " 'This module provides an introduction to the basic concepts and use of the Python programming language in support of translation.',\n", - " 'Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.']" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Source text\n", - "from nltk.tokenize import sent_tokenize\n", - "split_st_1 = sent_tokenize(st_1, language = 'english')\n", - "split_st_1" - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "id": "1cca97c2", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['Introduction au machine learning à l’aide de Python.',\n", - " 'Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction.',\n", - " 'L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.']" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "# Target text\n", - "from nltk.tokenize import sent_tokenize\n", - "split_tt_1 = sent_tokenize(tt_1, language = 'french')\n", - "split_tt_1" - ] - }, - { - "cell_type": "markdown", - "id": "af933044", - "metadata": {}, - "source": [ - "## Step four: Aligning those lists and exporting the tuples list to a csv-file\n", - "For this step, we'll need the `csv` module:" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "d720a8c3", - "metadata": {}, - "outputs": [], - "source": [ - "import csv" - ] - }, - { - "cell_type": "markdown", - "id": "3649fd6e", - "metadata": {}, - "source": [ - "A csv-file consists of rows, often a first header row with the label of each column, followed by the actual content of the file. So, in the first two lines we need to define what each row will contain.\n", - "- The content of the header row (stored in the variable `header`) simply consists of the language codes of the source and target language.\n", - "- The content of the next rows (stored in the variable `tm_1`) contains our texts.\n", - " - The `zip()`-function aligns the first sentence of the source text with the first sentence of the target text, the second with the second... and so on.\n", - " - The `list()`-function ensures that the `tm_1`-variable contains a list of tuples, not a generator (because the `zip()`-function creates a generator).\n", - " \n", - "Once we know what will go into the file, it's time to actually write the file.\n", - "- We open a new file, which gets a name, the `'w'`-command (meaning that it's meant to write a file) and an encoding (utf-8).\n", - "- Then, we define a csv-writer (very creatively called `write`).\n", - "- Lastly, we write the first row (the header) followed by the rest (the actual TM)." - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "54465bd4", - "metadata": {}, - "outputs": [], - "source": [ - "header = ['EN', 'FR'] # header row\n", - "tm_1 = list(zip(split_st_1, split_tt_1)) # rest of the file\n", - "\n", - "with open('translation-memory_1.csv', 'w', encoding = 'utf-8') as f:\n", - " write = csv.writer(f)\n", - " \n", - " write.writerow(header)\n", - " write.writerows(tm_1)" - ] - }, - { - "cell_type": "markdown", - "id": "aa373dde", - "metadata": {}, - "source": [ - "## Step five: Admiring our work\n", - "Using pandas, we can read the newly created csv-file." - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "dbbcea9d", - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "ac2fe189", - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
ENFR
0Introduction to Machine Learning with Python.Introduction au machine learning à l’aide de P...
1This module provides an introduction to the ba...Ce module offre une introduction aux concepts ...
2Focus lies on the main concepts that include N...L’accent est mis sur le traitement du langage ...
\n", - "
" - ], - "text/plain": [ - " EN \\\n", - "0 Introduction to Machine Learning with Python. \n", - "1 This module provides an introduction to the ba... \n", - "2 Focus lies on the main concepts that include N... \n", - "\n", - " FR \n", - "0 Introduction au machine learning à l’aide de P... \n", - "1 Ce module offre une introduction aux concepts ... \n", - "2 L’accent est mis sur le traitement du langage ... " - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "read_tm = pd.read_csv('translation-memory_1.csv', sep = ',')\n", - "read_tm.head()" - ] - }, - { - "cell_type": "markdown", - "id": "dca298fe", - "metadata": {}, - "source": [ - "(Next step: figuring out how to make pandas display the whole text.)" - ] - }, - { - "cell_type": "markdown", - "id": "9ddb159f", - "metadata": {}, - "source": [ - "## Step six: Importing the csv-file into a CAT-tool TM" - ] - }, - { - "cell_type": "markdown", - "id": "2c456496", - "metadata": {}, - "source": [ - "(I would need to swtich to Windows to show that.)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.16" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From aca8780442cc72c1375e1ad7593d9a7fab3913b3 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:54:48 +0200 Subject: [PATCH 31/46] Deleting presentation files --- python_en.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 python_en.txt diff --git a/python_en.txt b/python_en.txt deleted file mode 100644 index 93484cf..0000000 --- a/python_en.txt +++ /dev/null @@ -1 +0,0 @@ -Introduction to Machine Learning with Python. This module provides an introduction to the basic concepts and use of the Python programming language in support of translation. Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning. \ No newline at end of file From 27ff0d12410d58a4128670367ab2e162d28a5ce3 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:54:59 +0200 Subject: [PATCH 32/46] Deleting presentation files --- translation-memory_1.csv | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 translation-memory_1.csv diff --git a/translation-memory_1.csv b/translation-memory_1.csv deleted file mode 100644 index ae72263..0000000 --- a/translation-memory_1.csv +++ /dev/null @@ -1,4 +0,0 @@ -EN,FR -Introduction to Machine Learning with Python.,Introduction au machine learning à l’aide de Python. -This module provides an introduction to the basic concepts and use of the Python programming language in support of translation.,Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. -"Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.","L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning." From fefca57ffe0d2d4d055402c63a88a7f1a314500b Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:55:10 +0200 Subject: [PATCH 33/46] Deleting presentation files --- translation_agency_projects.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 translation_agency_projects.json diff --git a/translation_agency_projects.json b/translation_agency_projects.json deleted file mode 100644 index 211e436..0000000 --- a/translation_agency_projects.json +++ /dev/null @@ -1 +0,0 @@ -[{"translator": "Internal", "revisor": "Internal", "status": "delivered", "title": "La polyarthrite rhumato\u00efde et autres rhumatismes inflammatoires", "client": "Reuma vzw", "source": "FR", "target": "NL", "words": 2131, "start": "2020-09-24", "deadline": "2020-10-15", "price": 210, "tm": false, "domain": "healthcare"}, {"translator": "Sibylle", "revisor": "Internal", "status": "delayed", "title": "Handboek voor studentenvertegenwoordigers", "client": "KU Leuven", "source": "NL", "target": "EN", "words": 3654, "start": "2023-02-21", "deadline": "2023-03-02", "price": 540, "tm": true, "domain": "education"}, {"translator": "Internal", "revisor": "Sibylle", "status": "cancelled", "title": "User Guide MFPs", "client": "UGent", "source": "EN", "target": "NL", "words": 1852, "start": "2023-04-12", "deadline": "2023-04-14", "price": 280, "tm": true, "domain": ""}, {"translator": "Sibylle", "revisor": "Natacha", "status": "in revision", "title": "Guide de Bruxelles", "client": "Foodies", "source": "NL", "target": "FR", "words": 11500, "start": "2023-04-06", "deadline": "2023-05-27", "price": 1610, "tm": false, "domain": ""}] \ No newline at end of file From 9744a1d8fe4e511450b2eb1f2c374e06ce09ec13 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:55:20 +0200 Subject: [PATCH 34/46] Deleting presentation files --- translation_projects.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 translation_projects.json diff --git a/translation_projects.json b/translation_projects.json deleted file mode 100644 index 70522a0..0000000 --- a/translation_projects.json +++ /dev/null @@ -1 +0,0 @@ -[{"title": "La polyarthrite rhumato\u00efde et autres rhumatismes inflammatoires", "client": "Reuma vzw", "source": "FR", "target": "NL", "words": 2131, "start": "2020-09-24", "deadline": "2020-10-15", "price": 210, "tm": false, "domain": "healthcare"}, {"title": "Handboek voor studentenvertegenwoordigers", "client": "KU Leuven", "source": "NL", "target": "EN", "words": 3654, "start": "2023-02-21", "deadline": "2023-03-02", "price": 540, "tm": true, "domain": "education"}, {"title": "User Guide MFPs", "client": "UGent", "source": "EN", "target": "NL", "words": 1852, "start": "2023-04-14", "deadline": "2023-04-16", "price": 280, "tm": true, "domain": ""}, {"title": "Guide de Bruxelles", "client": "Foodies", "source": "NL", "target": "FR", "words": 11500, "start": "2023-03-22", "deadline": "2023-05-06", "price": 1610, "tm": false, "domain": ""}] \ No newline at end of file From f6b241f412160f0f12bd4a5329bbc2e1dff29be6 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:55:30 +0200 Subject: [PATCH 35/46] Deleting presentation files --- python_fr.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 python_fr.txt diff --git a/python_fr.txt b/python_fr.txt deleted file mode 100644 index c969aa6..0000000 --- a/python_fr.txt +++ /dev/null @@ -1 +0,0 @@ -Introduction au machine learning à l’aide de Python. Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning. \ No newline at end of file From 09fa5619f0e1f280238c0825404611f82f45c1c2 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:56:04 +0200 Subject: [PATCH 36/46] Deleting draft files --- Final-assignment_trial-and-error.ipynb | 259 ------------------------- 1 file changed, 259 deletions(-) delete mode 100644 Final-assignment_trial-and-error.ipynb diff --git a/Final-assignment_trial-and-error.ipynb b/Final-assignment_trial-and-error.ipynb deleted file mode 100644 index 5d24822..0000000 --- a/Final-assignment_trial-and-error.ipynb +++ /dev/null @@ -1,259 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "cf98a968", - "metadata": {}, - "outputs": [], - "source": [ - "import datetime #datetime package to convert strings into dates, calculate time periods etc." - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "ffae2adb", - "metadata": {}, - "outputs": [], - "source": [ - "class Translation:\n", - " translator = \"Internal\" # class attribute\n", - " revisor = \"Internal\"\n", - " status = \"created\"\n", - "\n", - " def _is_valid_status(self, status):\n", - " if not status.lower() in [\"created\",\n", - " \"in translation\",\n", - " \"in revision\",\n", - " \"delivered\",\n", - " \"delayed\",\n", - " \"cancelled\",\n", - " \"canceled\"]:\n", - " return \"Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.\"\n", - "\n", - " def __init__(self, title, client, source, target, words, start, deadline, price, tm, domain = ''):\n", - " # 'self' represents the object (= class element) itself\n", - " self.title = title\n", - " self.client = client\n", - " self.source = source\n", - " self.target = target\n", - " self.words = words\n", - " self.start = start\n", - " self.deadline = deadline\n", - " self.price = price\n", - " self.tm = tm\n", - " self.domain = domain\n", - " \n", - " today = datetime.date.today() # current date\n", - " self.st = datetime.date.fromisoformat(start) # turns string into date\n", - " self.dl = datetime.date.fromisoformat(deadline) # turns string into date\n", - " self.daysleft = self.dl - today # difference between deadline and current date\n", - " self.length = self.dl - self.st # difference between deadline and start date\n", - " self.rate = self.price/self.words # word rate (price divided by word count)\n", - " self.efficiency = words/self.length.days # words to translate per day (word count divided by project length, see 'length' in explanations above)\n", - "\n", - " def days_left(self):\n", - " # prints a text indicating how many days are left until the project deadline\n", - " if self.dl < datetime.date.today():\n", - " # if the deadline is in the past\n", - " return f\"The deadline has been exceeded already.\"\n", - " else:\n", - " # if the deadline is not in the past\n", - " return f\"There are {self.daysleft.days} days left until the deadline.\"\n", - " \n", - " def length(self):\n", - " return f\"{self.length.days} days\"\n", - " \n", - " def __str__(self):\n", - " # defines the print behaviour: returns a text providing the main information about the project\n", - " sent_1 = f\"{self.title} is a translation for {self.client} from {self.source} into {self.target}.\"\n", - " if self.translator == \"Internal\" and self.revisor == \"Internal\":\n", - " sent_2 = f\"Both the translator and the revisor are agency collaborators.\"\n", - " elif self.translator == \"Internal\" and self.revisor != \"Internal\":\n", - " sent_2 = f\"The translator is an agency collaborator and the revisor is {self.revisor}.\"\n", - " elif self.translator != \"Internal\" and self.revisor == \"Internal\":\n", - " sent_2 = f\"The translator is {self.translator} and the revisor is an agency collaborator.\"\n", - " else:\n", - " sent_2 = f\"The translator is {self.translator} and the revisor is {self.revisor}.\"\n", - " # this if-statement considers whether a domain was added\n", - " if len(self.domain) > 0:\n", - " sent_3 = f\"The domain is: {self.domain}.\"\n", - " else:\n", - " sent_3 = \"The domain is unspecified.\" # if no domain was added, the text mentions it\n", - " sent_4 = f\"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word.\" #the word rate is rounded to two decimal places to avoid cumbersomely long numbers\n", - " # this if-statement considers whether the deadline is in the past\n", - " if self.dl < datetime.date.today():\n", - " sent_5 = f\"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day.\" # the efficiency is rounded to units because you can't translate a fraction of a word anyway\n", - " else:\n", - " sent_5 = f\"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day.\"\n", - " # this if-statement considers whether there is a translation memory for the project\n", - " if self.tm is True:\n", - " sent_6 = f\"There is a translation memory.\"\n", - " else:\n", - " sent_6 = f\"There is no translation memory\"\n", - " sent_7 = f\"The project is currently {self.status}.\"\n", - " # print each sentence in a different line\n", - " return \"\\n\".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7])" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "086c20b8", - "metadata": {}, - "outputs": [], - "source": [ - "test = Translation('Guide de Bruxelles', 'Foodies', 'NL', 'FR', 11500, '2023-03-22', '2023-05-06', 1610, False)" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "a342339f", - "metadata": {}, - "outputs": [], - "source": [ - "test.status = \"in translation\"" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "id": "1334e6a5", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'ongoing'" - ] - }, - "execution_count": 10, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "test.status" - ] - }, - { - "cell_type": "code", - "execution_count": 19, - "id": "f308ff5a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.'" - ] - }, - "execution_count": 19, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "test._is_valid_status(\"ongoing\")" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "e7262ea0", - "metadata": {}, - "outputs": [ - { - "ename": "TypeError", - "evalue": "'datetime.timedelta' object is not callable", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[22], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mtest\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlength\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n", - "\u001b[0;31mTypeError\u001b[0m: 'datetime.timedelta' object is not callable" - ] - } - ], - "source": [ - "test.length()" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "8a4974ea", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", - "Both the translator and the revisor are agency collaborators.\n", - "The domain is unspecified.\n", - "It's 11500 words long, with a rate of 0.14 € per word.\n", - "It started on 2023-03-22 and was due on 2023-05-06, so 45 days were foreseen for it. To meet the deadline, 256 words needed to be translated or revised per day.\n", - "There is no translation memory\n", - "The project is currently in translation.\n" - ] - } - ], - "source": [ - "print(test)" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "3f06d6c4", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'2023-03-22'" - ] - }, - "execution_count": 26, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "test.start" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "4c9e596c", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.16" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 3d7b0d407820d163ca3a94507205ac284c45db34 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:56:15 +0200 Subject: [PATCH 37/46] Deleting draft files --- ...nt_trial-and-error_7_linking classes.ipynb | 669 ------------------ 1 file changed, 669 deletions(-) delete mode 100644 Final-assignment_trial-and-error_7_linking classes.ipynb diff --git a/Final-assignment_trial-and-error_7_linking classes.ipynb b/Final-assignment_trial-and-error_7_linking classes.ipynb deleted file mode 100644 index 7839b2f..0000000 --- a/Final-assignment_trial-and-error_7_linking classes.ipynb +++ /dev/null @@ -1,669 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "e8373e39", - "metadata": {}, - "source": [ - "# Import modules" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "b44e6905", - "metadata": {}, - "outputs": [], - "source": [ - "import datetime #datetime package to convert strings into dates, calculate time periods etc.\n", - "import re" - ] - }, - { - "cell_type": "markdown", - "id": "b73604ff", - "metadata": {}, - "source": [ - "# Freelancers" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "f5545676", - "metadata": {}, - "outputs": [], - "source": [ - "class Freelancers:\n", - " is_internal = False\n", - " \n", - " def __init__(self, name, email, phone, id):\n", - " self.name = name\n", - " self.email = email\n", - " self.phone = phone\n", - " self.id = id\n", - " \n", - " self.task = set()\n", - " self.language = set()\n", - " \n", - " def add_task(self, task):\n", - " self.task.add(task)\n", - " \n", - " def add_language(self, language):\n", - " self.language.add(language)\n", - " \n", - " def __str__(self):\n", - " sent_1 = f\"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone}).\"\n", - " sent_2 = f\"{self.name}'s ID in our database is {self.id}.\"\n", - " sent_3 = f\"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}.\"\n", - " return \"\\n\".join([sent_1, sent_2, sent_3])" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "id": "559edff4", - "metadata": {}, - "outputs": [], - "source": [ - "sdw_info = {\n", - " 'name' : 'Sibylle de Woot',\n", - " 'email' : 'sdewoot@email.be',\n", - " 'phone' : '+32 485 12 34 56',\n", - " 'id' : 'sdw',\n", - " 'task' : [\n", - " 'translator',\n", - " 'reviewer'\n", - " ],\n", - " 'language' : [\n", - " 'FR',\n", - " 'NL',\n", - " 'EN',\n", - " 'DE'\n", - " ]\n", - "}\n", - "mm_info = {\n", - " 'name' : 'Mariana Montes',\n", - " 'email' : 'mariana.montes@company.com',\n", - " 'phone' : '+32 487 98 76 54',\n", - " 'id' : 'mm',\n", - " 'task' : [\n", - " 'reviewer'\n", - " ],\n", - " 'language' : [\n", - " 'ES',\n", - " 'EN'\n", - " ]\n", - "}\n", - "evdl_info = {\n", - " 'name' : 'Emily van der Londen',\n", - " 'email' : 'evdl@translation.net',\n", - " 'phone' : '+32 486 19 28 37',\n", - " 'id' : 'evdl',\n", - " 'task' : [\n", - " 'translator'\n", - " ],\n", - " 'language' : [\n", - " 'NL',\n", - " 'EN',\n", - " 'FR'\n", - " ]\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "a5d872cd", - "metadata": {}, - "outputs": [], - "source": [ - "freelancers = [sdw_info, mm_info, evdl_info]" - ] - }, - { - "cell_type": "markdown", - "id": "f8f569ba", - "metadata": {}, - "source": [ - "## Class instances from dictionary" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "id": "15edcaa7", - "metadata": {}, - "outputs": [], - "source": [ - "class Freelancer():\n", - " def __init__(self, dictionary):\n", - "\n", - " for key, value in dictionary.items():\n", - " setattr(self, key, value)\n", - " \n", - " def __str__(self):\n", - " sent_1 = f\"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone}).\"\n", - " sent_2 = f\"{self.name}'s ID in our database is {self.id}.\"\n", - " sent_3 = f\"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}.\"\n", - " return \"\\n\".join([sent_1, sent_2, sent_3])\n", - "\n", - "sdw = Freelancer(sdw_info)\n", - "mm = Freelancer(mm_info)\n", - "evdl = Freelancer(evdl_info)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "ae186e5a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Sibylle de Woot'" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sdw.name" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "f22ef8fb", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "['translator', 'reviewer']" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sdw.task" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "08aa579e", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Sibylle de Woot can be contacted via e-mail (sdewoot@email.be) or via phone (+32 485 12 34 56).\n", - "Sibylle de Woot's ID in our database is sdw.\n", - "Sibylle de Woot works as a translator, reviewer for FR, NL, EN, DE.\n" - ] - } - ], - "source": [ - "print(sdw)" - ] - }, - { - "cell_type": "markdown", - "id": "1dccc238", - "metadata": {}, - "source": [ - "# Projects" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "id": "ffae2adb", - "metadata": {}, - "outputs": [], - "source": [ - "class Project:\n", - "\n", - " def __init__(self, title, client, source, target, words, start, deadline, price, tm, translator = 'internal', reviewer = 'internal', status = 'created', domain = ''):\n", - " \"\"\"Initialises an object of the Project class, a class that represents a translation project of a translation agency.\n", - " \n", - " Args:\n", - " title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated).\n", - " client (str): Client who ordered the translation.\n", - " source (str): Language of the source document(s) (document(s) to be translated).\n", - " target (str): Language of the target document(s) (translation(s)).\n", - " words (int): Word count of the source document(s).\n", - " start (str): Project's start date in ISO format (YYYY-MM-DD).\n", - " deadline (str): Project's deadline in ISO format (YYYY-MM-DD).\n", - " price (float): Total price invoiced to the client (excl. VAT).\n", - " tm (bool): Whether a translation memory is available for this project.\n", - " translator (string, optional): Translator assigned to the project. Defaults to 'internal'.\n", - " reviewer (string, optional): Reviewer assigned to the project. Defaults to 'internal'.\n", - " status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'.\n", - " domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string.\n", - " \n", - " Attributes:\n", - " title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated).\n", - " client (str): Client who ordered the translation.\n", - " source (str): Language of the source document(s) (document(s) to be translated).\n", - " target (str): Language of the target document(s) (translation(s)).\n", - " words (int): Word count of the source document(s).\n", - " start (str): Project's start date in ISO format (YYYY-MM-DD).\n", - " deadline (str): Project's deadline in ISO format (YYYY-MM-DD).\n", - " price (float): Total price invoiced to the client (excl. VAT).\n", - " tm (bool): Whether a translation memory is available for this project.\n", - " translator (string, optional): Translator assigned to the project.\n", - " reviewer (string, optional): Reviewer assigned to the project.\n", - " status (string, optional): Current project status inside the agency's workflow.\n", - " domain (str): Overall domain to which the project belongs.\n", - " today (date): Date of the day where the script is run.\n", - " st (date): Project's start date, turned from an ISO-formatted string into a date.\n", - " dl (date): Project's deadline, turned from an ISO-formatted string into a date.\n", - " daysleft (timedelta): Time left until the project's deadline.\n", - " length (timedelta): Total time allocated to the project.\n", - " rate (float): Project's word rate.\n", - " efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek).\n", - " \n", - " Raises:\n", - " TypeError: If 'title' is not a string.\n", - " TypeError: If 'client' is not a string.\n", - " TypeError: If 'source' is not a string.\n", - " TypeError: If 'target' is not a string.\n", - " TypeError: If 'words' is not an integer.\n", - " TypeError: If 'start' is not a string.\n", - " ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits.\n", - " TypeError: If 'deadline' is not a string.\n", - " ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits.\n", - " TypeError: If 'price' is not a float.\n", - " TypeError: If 'tm' is not a boolean.\n", - " TypeError: If 'status' is not a string.\n", - " ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed.\n", - " TypeError: If 'domain' is not a string.\n", - " \"\"\"\n", - " if type(title) != str:\n", - " raise TypeError(\"The title must be a string.\")\n", - " else:\n", - " self.title = title\n", - " if type(client) != str:\n", - " raise TypeError(\"The client name must be a string.\")\n", - " else:\n", - " self.client = client\n", - " if type(source) != str:\n", - " raise TypeError(\"The source language must be a string.\")\n", - " else:\n", - " self.source = source\n", - " if type(target) != str:\n", - " raise TypeError(\"The target langague must be a string.\")\n", - " else:\n", - " self.target = target\n", - " if type(words) != int:\n", - " raise TypeError(\"The word count must be an integer.\")\n", - " else:\n", - " self.words = words\n", - " if type(start) != str:\n", - " raise TypeError(\"The start date must be provided as a string.\")\n", - " elif not re.match (\"[0-9]{4}-[0-9]{2}-[0-9]{2}\", start):\n", - " raise ValueError(\"A valid start date must be provided in ISO format.\")\n", - " else:\n", - " self.start = start\n", - " if type(deadline) != str:\n", - " raise TypeError(\"The deadline must be provided as a string.\")\n", - " elif not re.match (\"[0-9]{4}-[0-9]{2}-[0-9]{2}\", deadline):\n", - " raise ValueError(\"A valid deadline must be provided in ISO format.\")\n", - " else:\n", - " self.deadline = deadline\n", - " if type(price) != float:\n", - " raise TypeError(\"The price must be a float.\")\n", - " else:\n", - " self.price = price\n", - " if type(tm) != bool:\n", - " raise TypeError(\"The TM availability must be a boolean.\")\n", - " else:\n", - " self.tm = tm\n", - " if translator == '':\n", - " self.translator = \"internal\"\n", - " else:\n", - " self.translator = translator\n", - " if reviewer == '':\n", - " self.reviewer = \"internal\"\n", - " else:\n", - " self.reviewer = reviewer\n", - " if type(status) != str:\n", - " raise TypeError(\"The status must be a string.\")\n", - " elif status == '':\n", - " self.status = \"created\"\n", - " elif not status.lower() in [\"created\",\n", - " \"in translation\",\n", - " \"in revision\",\n", - " \"delivered\",\n", - " \"delayed\",\n", - " \"cancelled\",\n", - " \"canceled\"]:\n", - " raise ValueError(\"Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.\")\n", - " else:\n", - " self.status = status\n", - " if type(domain) != str:\n", - " raise TypeError(\"The domain must be a string.\")\n", - " else:\n", - " self.domain = domain\n", - " \n", - " today = datetime.date.today()\n", - " self.st = datetime.date.fromisoformat(start)\n", - " self.dl = datetime.date.fromisoformat(deadline)\n", - " self.daysleft = self.dl - today\n", - " self.length = self.dl - self.st\n", - " self.rate = self.price/self.words\n", - " self.efficiency = words/((5/7)*self.length.days)\n", - "\n", - " def days_left(self):\n", - " \"\"\"Displays how many days remain until the project deadline.\n", - " \n", - " Args:\n", - " None\n", - " \n", - " Returns:\n", - " str: A message informing the user that the deadline has been exceeded already if the deadline is in the past.\n", - " str: A message informing the user of the number of days left until the deadline if the deadline is not in the past.\n", - " \"\"\"\n", - " # prints a text indicating how many days are left until the project deadline\n", - " if self.dl < datetime.date.today():\n", - " # if the deadline is in the past\n", - " return f\"The deadline has been exceeded already.\"\n", - " else:\n", - " # if the deadline is not in the past\n", - " return f\"There are {self.daysleft.days} days left until the deadline.\"\n", - " \n", - " def project_length(self):\n", - " \"\"\"Displays the total number of days allocated to the project.\n", - " \n", - " Args:\n", - " None\n", - " \n", - " Returns:\n", - " str: The total number of days allocated to the project.\n", - " \"\"\"\n", - " return f\"{self.length.days} days\"\n", - " \n", - " def __str__(self):\n", - " # defines the print behaviour: returns a text providing the main information about the project\n", - " sent_1 = f\"{self.title} is a translation for {self.client} from {self.source} into {self.target}.\"\n", - " if self.translator == \"internal\" and self.reviewer == \"internal\":\n", - " sent_2 = f\"Both the translator and the reviewer are agency collaborators.\"\n", - " elif self.translator == \"internal\" and self.reviewer != \"internal\":\n", - " sent_2 = f\"The translator is an agency collaborator and the reviewer is {self.reviewer.name}.\"\n", - " elif self.translator != \"internal\" and self.reviewer == \"internal\":\n", - " sent_2 = f\"The translator is {self.translator.name} and the reviewer is an agency collaborator.\"\n", - " else:\n", - " sent_2 = f\"The translator is {self.translator.name} and the reviewer is {self.reviewer.name}.\"\n", - " # this if-statement considers whether a domain was added\n", - " if len(self.domain) > 0:\n", - " sent_3 = f\"The domain is: {self.domain}.\"\n", - " else:\n", - " sent_3 = \"The domain is unspecified.\" # if no domain was added, the text mentions it\n", - " sent_4 = f\"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word.\" #the word rate is rounded to two decimal places to avoid cumbersomely long numbers\n", - " # this if-statement considers whether the deadline is in the past\n", - " if self.dl < datetime.date.today():\n", - " sent_5 = f\"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day.\" # the efficiency is rounded to units because you can't translate a fraction of a word anyway\n", - " else:\n", - " sent_5 = f\"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day.\"\n", - " # this if-statement considers whether there is a translation memory for the project\n", - " sent_6 = f\"There is {'a' if self.tm else 'no'} translation memory.\"\n", - " sent_7 = f\"The project is currently {self.status}.\"\n", - " # print each sentence in a different line\n", - " return \"\\n\".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7])" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "af6d3dd5", - "metadata": {}, - "outputs": [], - "source": [ - "rhumatismes_inflammatoires = {\n", - " 'title' : 'La polyarthrite rhumatoïde et autres rhumatismes inflammatoires',\n", - " 'client' : 'Reuma vzw',\n", - " 'source' : 'FR',\n", - " 'target' : 'NL',\n", - " 'words' : 2131,\n", - " 'start' : '2020-09-24',\n", - " 'deadline' : '2020-10-15',\n", - " 'price' : 210.00,\n", - " 'tm' : False,\n", - " 'translator' : '',\n", - " 'reviewer' : '',\n", - " 'status' : '',\n", - " 'domain' : 'healthcare'\n", - "}\n", - "handboek = {\n", - " 'title' : 'Handboek voor studentenvertegenwoordigers',\n", - " 'client' : 'KU Leuven',\n", - " 'source' : 'NL',\n", - " 'target' : 'EN',\n", - " 'words' : 3654,\n", - " 'start' : '2023-02-21',\n", - " 'deadline' : '2023-03-02',\n", - " 'price' : 540.00,\n", - " 'tm' : True,\n", - " 'translator' : sdw,\n", - " 'reviewer' : '',\n", - " 'status' : 'delayed', \n", - " 'domain' : 'education'\n", - "}\n", - "user_guide = {\n", - " 'title' : 'User Guide MFPs',\n", - " 'client' : 'UGent',\n", - " 'source' : 'EN',\n", - " 'target' : 'NL',\n", - " 'words' : 1852,\n", - " 'start' : '2023-04-12',\n", - " 'deadline' : '2023-04-14',\n", - " 'price' : 280.00,\n", - " 'tm' : True,\n", - " 'translator' : '',\n", - " 'reviewer' : mm,\n", - " 'status' : 'cancelled',\n", - " 'domain' : ''\n", - "}\n", - "guide_bruxelles = {\n", - " 'title' : 'Guide de Bruxelles',\n", - " 'client' : 'Foodies',\n", - " 'source' : 'NL',\n", - " 'target' : 'FR',\n", - " 'words' : 11500,\n", - " 'start' : '2023-04-06',\n", - " 'deadline' : '2023-05-27',\n", - " 'price' : 1610.00,\n", - " 'tm' : False,\n", - " 'translator' : evdl,\n", - " 'reviewer' : sdw,\n", - " 'status' : 'in revision', \n", - " 'domain' : ''\n", - "}" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "9c855697", - "metadata": {}, - "outputs": [], - "source": [ - "translation_projects = [rhumatismes_inflammatoires, handboek, user_guide, guide_bruxelles]" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "23542b23", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "La polyarthrite rhumatoïde et autres rhumatismes inflammatoires is a translation for Reuma vzw from FR into NL.\n", - "Both the translator and the reviewer are agency collaborators.\n", - "The domain is: healthcare.\n", - "It's 2131 words long, with a rate of 0.1 € per word.\n", - "It started on 2020-09-24 and was due on 2020-10-15, so 21 days were foreseen for it. To meet the deadline, 142 words needed to be translated or revised per day.\n", - "There is no translation memory.\n", - "The project is currently created.\n", - "----\n", - "Handboek voor studentenvertegenwoordigers is a translation for KU Leuven from NL into EN.\n", - "The translator is Sibylle de Woot and the reviewer is an agency collaborator.\n", - "The domain is: education.\n", - "It's 3654 words long, with a rate of 0.15 € per word.\n", - "It started on 2023-02-21 and was due on 2023-03-02, so 9 days were foreseen for it. To meet the deadline, 568 words needed to be translated or revised per day.\n", - "There is a translation memory.\n", - "The project is currently delayed.\n", - "----\n", - "User Guide MFPs is a translation for UGent from EN into NL.\n", - "The translator is an agency collaborator and the reviewer is Mariana Montes.\n", - "The domain is unspecified.\n", - "It's 1852 words long, with a rate of 0.15 € per word.\n", - "It started on 2023-04-12 and was due on 2023-04-14, so 2 days were foreseen for it. To meet the deadline, 1296 words needed to be translated or revised per day.\n", - "There is a translation memory.\n", - "The project is currently cancelled.\n", - "----\n", - "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", - "The translator is Emily van der Londen and the reviewer is Sibylle de Woot.\n", - "The domain is unspecified.\n", - "It's 11500 words long, with a rate of 0.14 € per word.\n", - "It started on 2023-04-06 and was due on 2023-05-27, so 51 days were foreseen for it. To meet the deadline, 316 words needed to be translated or revised per day.\n", - "There is no translation memory.\n", - "The project is currently in revision.\n", - "----\n" - ] - } - ], - "source": [ - "for project in translation_projects:\n", - " my_project = Project(project['title'], project['client'], project['source'], project['target'], project['words'], project['start'], project['deadline'], project['price'], project['tm'], project['translator'], project['reviewer'], project['status'], project['domain'])\n", - " print(my_project)\n", - " print('----')" - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "29975a1e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "1610.0" - ] - }, - "execution_count": 21, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_project.price" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "99078130", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "<__main__.Freelancer at 0x7fe637178a00>" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_project.reviewer" - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "35e7b92c", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'Sibylle de Woot'" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_project.reviewer.name" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "0332a787", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "'sdewoot@email.be'" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "my_project.reviewer.email" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "1f569b3f", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.16" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 9b84b9cfcee088c04dd287bee59ade28f4e7ddba Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:56:26 +0200 Subject: [PATCH 38/46] Deleting draft files --- ...nt_trial-and-error_8_linking classes_v2.py | 534 ------------------ 1 file changed, 534 deletions(-) delete mode 100644 Final-assignment_trial-and-error_8_linking classes_v2.py diff --git a/Final-assignment_trial-and-error_8_linking classes_v2.py b/Final-assignment_trial-and-error_8_linking classes_v2.py deleted file mode 100644 index 67aa618..0000000 --- a/Final-assignment_trial-and-error_8_linking classes_v2.py +++ /dev/null @@ -1,534 +0,0 @@ -#!/usr/bin/env python -# coding: utf-8 - -# # Import modules - -# In[1]: - - -import datetime #datetime package to convert strings into dates, calculate time periods etc. -import re -import json - - -# # Freelancers - -# In[12]: - - -class Freelancers: - is_internal = False - - def __init__(self, name, email, phone, ref): - self.name = name - self.email = email - self.phone = phone - self.ref = ref - - self.task = set() - self.language = set() - - def add_task(self, task): - self.task.add(task) - - def add_language(self, language): - self.language.add(language) - - def __str__(self): - sent_1 = f"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone})." - sent_2 = f"{self.name}'s ID in our database is {self.ref}." - sent_3 = f"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}." - return "\n".join([sent_1, sent_2, sent_3]) - - -# In[2]: - - -sdw = { - 'name' : 'Sibylle de Woot', - 'email' : 'sdewoot@email.be', - 'phone' : '+32 485 12 34 56', - 'ref' : 'sdw', - 'task' : [ - 'translator', - 'reviewer' - ], - 'language' : [ - 'FR', - 'NL', - 'EN', - 'DE' - ] -} -mm = { - 'name' : 'Mariana Montes', - 'email' : 'mariana.montes@company.com', - 'phone' : '+32 487 98 76 54', - 'ref' : 'mm', - 'task' : [ - 'reviewer' - ], - 'language' : [ - 'ES', - 'EN' - ] -} -evdl = { - 'name' : 'Emily van der Londen', - 'email' : 'evdl@translation.net', - 'phone' : '+32 486 19 28 37', - 'ref' : 'evdl', - 'task' : [ - 'translator' - ], - 'language' : [ - 'NL', - 'EN', - 'FR' - ] -} - - -# In[3]: - - -freelancers = [sdw, mm, evdl] - - -# In[6]: - - -with open('freelancers_db.json', 'w', encoding='utf-8') as f: - json.dump(freelancers, f) - - -# ## Class instances from dictionary - -# In[3]: - - -class Freelancer(): - def __init__(self, dictionary): - - for key, value in dictionary.items(): - setattr(self, key, value) - - def __str__(self): - sent_1 = f"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone})." - sent_2 = f"{self.name}'s ID in our database is {self.id}." - sent_3 = f"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}." - return "\n".join([sent_1, sent_2, sent_3]) - -sdw = Freelancer(sdw_info) -mm = Freelancer(mm_info) -evdl = Freelancer(evdl_info) - - -# In[20]: - - -sdw.name - - -# In[21]: - - -sdw.task - - -# In[25]: - - -print(sdw) - - -# # Class instances with kwargs - -# In[2]: - - -class Freelancer(): - - def __init__(self, name, email, phone, ref): - self.name = name - self.email = email - self.phone = phone - self.ref = ref - - self.task = set() - self.language = set() - - def add_task(self, task): - self.task.add(task) - - def add_language(self, language): - self.language.add(language) - - def function_with_kwargs(**kwargs): - if "name" in kwargs: - print(kwargs["name"]) - else: - print("no name found") - - def __str__(self): - sent_1 = f"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone})." - sent_2 = f"{self.name}'s ID in our database is {self.id}." - sent_3 = f"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}." - return "\n".join([sent_1, sent_2, sent_3]) - - -# # Projects - -# In[3]: - - -class Project: - - def __init__(self, title, client, source, target, words, start, deadline, price, tm, translator = 'internal', reviewer = 'internal', status = 'created', domain = ''): - """Initialises an object of the Project class, a class that represents a translation project of a translation agency. - - Args: - title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). - client (str): Client who ordered the translation. - source (str): Language of the source document(s) (document(s) to be translated). - target (str): Language of the target document(s) (translation(s)). - words (int): Word count of the source document(s). - start (str): Project's start date in ISO format (YYYY-MM-DD). - deadline (str): Project's deadline in ISO format (YYYY-MM-DD). - price (float): Total price invoiced to the client (excl. VAT). - tm (bool): Whether a translation memory is available for this project. - translator (string, optional): Translator assigned to the project. Defaults to 'internal'. - reviewer (string, optional): Reviewer assigned to the project. Defaults to 'internal'. - status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'. - domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string. - - Attributes: - title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). - client (str): Client who ordered the translation. - source (str): Language of the source document(s) (document(s) to be translated). - target (str): Language of the target document(s) (translation(s)). - words (int): Word count of the source document(s). - start (str): Project's start date in ISO format (YYYY-MM-DD). - deadline (str): Project's deadline in ISO format (YYYY-MM-DD). - price (float): Total price invoiced to the client (excl. VAT). - tm (bool): Whether a translation memory is available for this project. - translator (string, optional): Translator assigned to the project. - reviewer (string, optional): Reviewer assigned to the project. - status (string, optional): Current project status inside the agency's workflow. - domain (str): Overall domain to which the project belongs. - today (date): Date of the day where the script is run. - st (date): Project's start date, turned from an ISO-formatted string into a date. - dl (date): Project's deadline, turned from an ISO-formatted string into a date. - daysleft (timedelta): Time left until the project's deadline. - length (timedelta): Total time allocated to the project. - rate (float): Project's word rate. - efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek). - - Raises: - TypeError: If 'title' is not a string. - TypeError: If 'client' is not a string. - TypeError: If 'source' is not a string. - TypeError: If 'target' is not a string. - TypeError: If 'words' is not an integer. - TypeError: If 'start' is not a string. - ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits. - TypeError: If 'deadline' is not a string. - ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits. - TypeError: If 'price' is not a float. - TypeError: If 'tm' is not a boolean. - TypeError: If 'status' is not a string. - ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed. - TypeError: If 'domain' is not a string. - """ - if type(title) != str: - raise TypeError("The title must be a string.") - else: - self.title = title - if type(client) != str: - raise TypeError("The client name must be a string.") - else: - self.client = client - if type(source) != str: - raise TypeError("The source language must be a string.") - else: - self.source = source - if type(target) != str: - raise TypeError("The target langague must be a string.") - else: - self.target = target - if type(words) != int: - raise TypeError("The word count must be an integer.") - else: - self.words = words - if type(start) != str: - raise TypeError("The start date must be provided as a string.") - elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", start): - raise ValueError("A valid start date must be provided in ISO format.") - else: - self.start = start - if type(deadline) != str: - raise TypeError("The deadline must be provided as a string.") - elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", deadline): - raise ValueError("A valid deadline must be provided in ISO format.") - else: - self.deadline = deadline - if type(price) != float: - raise TypeError("The price must be a float.") - else: - self.price = price - if type(tm) != bool: - raise TypeError("The TM availability must be a boolean.") - else: - self.tm = tm - if type(translator) != str and type(translator) != Freelancer: - raise TypeError("The translator's name must be a string or an entry in our freelancer database.") - elif translator == '': - self.translator = "internal" - else: - self.translator = translator - if type(reviewer) != str and type(reviewer) != Freelancer: - raise TypeError("The reviewer's name must be a string or an entry in our freelancer database.") - elif reviewer == '': - self.reviewer = "internal" - else: - self.reviewer = reviewer - if type(status) != str: - raise TypeError("The status must be a string.") - elif status == '': - self.status = "created" - elif not status.lower() in ["created", - "in translation", - "in revision", - "delivered", - "delayed", - "cancelled", - "canceled"]: - raise ValueError("Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.") - else: - self.status = status - if type(domain) != str: - raise TypeError("The domain must be a string.") - else: - self.domain = domain - - today = datetime.date.today() - self.st = datetime.date.fromisoformat(start) - self.dl = datetime.date.fromisoformat(deadline) - self.daysleft = self.dl - today - self.length = self.dl - self.st - self.rate = self.price/self.words - self.efficiency = words/((5/7)*self.length.days) - - def days_left(self): - """Displays how many days remain until the project deadline. - - Args: - None - - Returns: - str: A message informing the user that the deadline has been exceeded already if the deadline is in the past. - str: A message informing the user of the number of days left until the deadline if the deadline is not in the past. - """ - # prints a text indicating how many days are left until the project deadline - if self.dl < datetime.date.today(): - # if the deadline is in the past - return f"The deadline has been exceeded already." - else: - # if the deadline is not in the past - return f"There are {self.daysleft.days} days left until the deadline." - - def project_length(self): - """Displays the total number of days allocated to the project. - - Args: - None - - Returns: - str: The total number of days allocated to the project. - """ - return f"{self.length.days} days" - - def __str__(self): - # defines the print behaviour: returns a text providing the main information about the project - sent_1 = f"{self.title} is a translation for {self.client} from {self.source} into {self.target}." - if self.translator == "internal" and self.reviewer == "internal": - sent_2 = f"Both the translator and the reviewer are agency collaborators." - elif self.translator == "internal" and self.reviewer != "internal": - sent_2 = f"The translator is an agency collaborator and the reviewer is {self.reviewer.name}." - elif self.translator != "internal" and self.reviewer == "internal": - sent_2 = f"The translator is {self.translator.name} and the reviewer is an agency collaborator." - else: - sent_2 = f"The translator is {self.translator.name} and the reviewer is {self.reviewer.name}." - # this if-statement considers whether a domain was added - if len(self.domain) > 0: - sent_3 = f"The domain is: {self.domain}." - else: - sent_3 = "The domain is unspecified." # if no domain was added, the text mentions it - sent_4 = f"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word." #the word rate is rounded to two decimal places to avoid cumbersomely long numbers - # this if-statement considers whether the deadline is in the past - if self.dl < datetime.date.today(): - sent_5 = f"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day." # the efficiency is rounded to units because you can't translate a fraction of a word anyway - else: - sent_5 = f"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day." - # this if-statement considers whether there is a translation memory for the project - sent_6 = f"There is {'a' if self.tm else 'no'} translation memory." - sent_7 = f"The project is currently {self.status}." - # print each sentence in a different line - return "\n".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7]) - - -# In[4]: - - -rhumatismes_inflammatoires = { - 'title' : 'La polyarthrite rhumatoïde et autres rhumatismes inflammatoires', - 'client' : 'Reuma vzw', - 'source' : 'FR', - 'target' : 'NL', - 'words' : 2131, - 'start' : '2020-09-24', - 'deadline' : '2020-10-15', - 'price' : 210.00, - 'tm' : False, - 'translator' : '', - 'reviewer' : '', - 'status' : '', - 'domain' : 'healthcare' -} -handboek = { - 'title' : 'Handboek voor studentenvertegenwoordigers', - 'client' : 'KU Leuven', - 'source' : 'NL', - 'target' : 'EN', - 'words' : 3654, - 'start' : '2023-02-21', - 'deadline' : '2023-03-02', - 'price' : 540.00, - 'tm' : True, - 'translator' : 'sdw', - 'reviewer' : '', - 'status' : 'delayed', - 'domain' : 'education' -} -user_guide = { - 'title' : 'User Guide MFPs', - 'client' : 'UGent', - 'source' : 'EN', - 'target' : 'NL', - 'words' : 1852, - 'start' : '2023-04-12', - 'deadline' : '2023-04-14', - 'price' : 280.00, - 'tm' : True, - 'translator' : '', - 'reviewer' : 'mm', - 'status' : 'cancelled', - 'domain' : '' -} -guide_bruxelles = { - 'title' : 'Guide de Bruxelles', - 'client' : 'Foodies', - 'source' : 'NL', - 'target' : 'FR', - 'words' : 11500, - 'start' : '2023-04-06', - 'deadline' : '2023-05-27', - 'price' : 1610.00, - 'tm' : False, - 'translator' : 'evdl', - 'reviewer' : 'sdw', - 'status' : 'in revision', - 'domain' : '' -} - - -# In[6]: - - -translation_projects = [rhumatismes_inflammatoires, handboek, user_guide, guide_bruxelles] - - -# In[16]: - - -for project in translation_projects: - my_project = Project(project['title'], project['client'], project['source'], project['target'], project['words'], project['start'], project['deadline'], project['price'], project['tm'], project['translator'], project['reviewer'], project['status'], project['domain']) - print(my_project) - print('----') - - -# In[21]: - - -my_project.price - - -# In[22]: - - -my_project.reviewer - - -# In[23]: - - -my_project.reviewer.name - - -# In[25]: - - -my_project.reviewer.email - - -# # Separate jsons projects - -# In[5]: - - -with open('proj_rhumatismes_inflammatoires.json', 'w', encoding='utf-8') as f: - json.dump(rhumatismes_inflammatoires, f) - - -# In[6]: - - -with open('proj_handboek.json', 'w', encoding='utf-8') as f: - json.dump(handboek, f) - - -# In[7]: - - -with open('proj_user_guide.json', 'w', encoding='utf-8') as f: - json.dump(user_guide, f) - - -# In[8]: - - -with open('proj_guide_bruxelles.json', 'w', encoding='utf-8') as f: - json.dump(guide_bruxelles, f) - - -# # Linking projects and freelancers - -# In[9]: - - -with open('freelancers_db.json', 'r') as f: - my_freelancers = {x.ref : x for x in json.load(f)} # if the json is a list of dictionaries -with open('proj_guide_bruxelles.json', 'r') as f2: - project_dict = json.load(f2) - if 'translator' in project_dict and project_dict['translator'] in my_freelancers: - project_dict['translator'] = Freelancer(**my_freelancers[project_dict['translator']]) - if 'reviewer' in project_dict and project_dict['reviewer'] in my_freelancers: - project_dict['reviewer'] = Freelancer(**my_freelancers[project_dict['reviewer']]) - project = Project(**project_dict) # of course you have to allow - - -# In[ ]: - - - - From f09dee479dd4d3310ed81c4726d92e5f67f97932 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:56:45 +0200 Subject: [PATCH 39/46] Deleting draft files --- README.md | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index c7b1b96..0000000 --- a/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# What I'm planning - -- Start from the translation job management script created for the first assignment, but expand on it to make it useful for a translation agency rather than an independent translator. -- Three class attributes (strings) : - - Translator - - Revisor - - Status - - -> The default value for "Translator" and "Revisor" is "Internal", meaning that an employee of the translation agency took up the job. If the agency assigned the job to a freelancer, the default value can be changed to their name. - - -> The default value for "Status" is "Created". The status can then be updated as the project progresses to "In translation", "In revision", "Delivered", "Delayed" or "Cancelled". If possible, the script should only accept these six labels to prevent organisational chaos due to everyone using their own labels. -- Instance and computed attributes remain the same as in the first assignment (with some edits to add the advice from the first assignment's feedback). -- Add validation for unexpected input (+ for "Status" labels different from the six authorised labels?) -- Add methods (?) to call the computed attributes and get a result that's more legible than what this currently generates (for example "22 days" instead of "datetime.timedelta(days=22)") -- The input will still be read from a list of dictionaries in a separate json-file. Those dictionaries will be described in a separate markdown file. - -# What I'd like to add but don't know how - -- It would be neat if I could do something with the translation memory and termbase of each project. Maybe add a method that opens them for a preview? -- It would also be super useful to have a way to align a source and a target text and generate a file that can be added to a translation memory. So, to start from two docx-files (or txt-files), split them into sentences and pair each sentence in the source text with the corresponding sentence in the target text and generate a single csv-file with the paired sentences. Ideally, the context (i.e. the surrounding sentences) should also be considered, but if that's not possible an aligned csv would already be awesome. - -# Things I'm not yet sure how to integrate into the assignment - -- Use of argparse. -- Use of regular expressions. From 58f2ff77608aa7677a78963600a8d32b7852ffc5 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:57:10 +0200 Subject: [PATCH 40/46] Deleting draft files --- TM-generator.md | 153 ------------------------------------------------ 1 file changed, 153 deletions(-) delete mode 100644 TM-generator.md diff --git a/TM-generator.md b/TM-generator.md deleted file mode 100644 index eab89b0..0000000 --- a/TM-generator.md +++ /dev/null @@ -1,153 +0,0 @@ -# Source and target text aligner -Sometimes, you still have some translations left over from a time where you didn't use CAT-tools and you'd like to feed them into your translation memory. Some CAT-tools have built-in text aligners, but not all of them, so how do you go from two separate text documents to an aligned bilingual (csv-)file ready to be fed into your TM? - -## Step one: Prepare the source and target text -The easiest file format to start from is a pure txt-file... and since for a TM only the pure text is of interest, converting a Word-, PowerPoint- or whatever file to a txt-file isn't an issue. So, we'll take the original source and target document and export them to a txt-format (with utf-8 encoding). -## Step two: Store the two (continuous) texts into variables - - -```python -with open('python_en.txt', encoding = 'utf-8') as f: - st_1 = f.read() -st_1 -``` - - - - - 'Introduction to Machine Learning with Python.\n\nThis module provides an introduction to the basic concepts and use of the Python programming language in support of translation. Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.\n' - - - - -```python -with open('python_fr.txt', encoding = 'utf-8') as f: - tt_1 = f.read() -tt_1 -``` - - - - - 'Introduction au machine learning à l’aide de Python.\n\nCe module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.\n' - - - -## Step three: Split the single text string into list of sentences -Since most TMs (and CAT-tools) use sentence segmentation, the source and target text need to be split up into sentences. So, each text becomes a list of separate sentences. - -For this, we use `nltk tokenizer`, which functions with English and French (and many other languages, but English and French are the ones that interest us right now). - - -```python -from nltk.tokenize import sent_tokenize -split_st_1 = sent_tokenize(st_1, language = 'english') -split_st_1 -``` - - - - - ['Introduction to Machine Learning with Python.', - 'This module provides an introduction to the basic concepts and use of the Python programming language in support of translation.', - 'Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.'] - - - - -```python -from nltk.tokenize import sent_tokenize -split_tt_1 = sent_tokenize(tt_1, language = 'french') -split_tt_1 -``` - - - - - ['Introduction au machine learning à l’aide de Python.', - 'Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction.', - 'L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.'] - - - -## Step four: Aligning those lists and exporting the tuples list to a csv-file -Writing a csv-file looks fairly similar to reading a txt-file, like we did at the start of the process, except this time we use `f.write` instead of `f.read`. -A csv-file consists of rows, often a first header row with the label of each column, followed by the actual content of the file. Both are defined separately. -- The content of the header row (defined in the first indented line) simply consists of the language codes of the source and target language. -- The content of the next rows (defined in the next indented lines) contains our texts. - - The `zip()`-function aligns the first sentence of the source text with the first sentence of the target text, the second with the second... and so on in x, y tuples. - - Next, those tuples are put inside an f-string that separates x and y with a tab and places makes a new line start after each y. - - -```python -with open("translation_memory.csv", "w", encoding = "utf-8") as f: # open file to overwrite - f.write("EN\tFR\n") # write heading - for x, y in zip(split_st_1, split_tt_1): # go through each pair of lines - f.write(f"{x}\t{y}\n") # write the line -``` - -## Step five: Admiring our work -Using pandas, we can read the newly created csv-file. - - -```python -import pandas as pd -``` - - -```python -read_tm = pd.read_csv('translation_memory.csv', sep = '\t') -read_tm.head() -``` - - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - -
ENFR
0Introduction to Machine Learning with Python.Introduction au machine learning à l’aide de P...
1This module provides an introduction to the ba...Ce module offre une introduction aux concepts ...
2Focus lies on the main concepts that include N...L’accent est mis sur le traitement du langage ...
-
- - - - -```python - -``` From 39285c5844fbbad6e1ac790f37c5bdc0d01d5633 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:57:19 +0200 Subject: [PATCH 41/46] Deleting draft files --- example.json | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 example.json diff --git a/example.json b/example.json deleted file mode 100644 index e69de29..0000000 From 4ea8c5357f201ddf71a23d3d348a12059c4f6373 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:57:29 +0200 Subject: [PATCH 42/46] Deleting draft files --- script-with-argparse.py | 230 ---------------------------------------- 1 file changed, 230 deletions(-) delete mode 100644 script-with-argparse.py diff --git a/script-with-argparse.py b/script-with-argparse.py deleted file mode 100644 index 27d3898..0000000 --- a/script-with-argparse.py +++ /dev/null @@ -1,230 +0,0 @@ -#! /usr/bin/python - -import datetime -import re - -"""This script gives a translation agency an overview of all its projects. -""" -import argparse - -class Project: - - def __init__( - self, - title, - client, - source, - target, - words, - start, - deadline, - price, - tm, - translator = 'internal', - revisor = 'internal', - status = 'created', - domain = ''): - """Initialises an object of the Project class, a class that represents a translation project of a translation agency. - - Args: - title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). - client (str): Client who ordered the translation. - source (str): Language of the source document(s) (document(s) to be translated). - target (str): Language of the target document(s) (translation(s)). - words (int): Word count of the source document(s). - start (str): Project's start date in ISO format (YYYY-MM-DD). - deadline (str): Project's deadline in ISO format (YYYY-MM-DD). - price (float): Total price invoiced to the client (excl. VAT). - tm (bool): Whether a translation memory is available for this project. - translator (string, optional): Translator assigned to the project. Defaults to 'internal'. - revisor (string, optional): Revisor assigned to the project. Defaults to 'internal'. - status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'. - domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string. - - Attributes: - title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). - client (str): Client who ordered the translation. - source (str): Language of the source document(s) (document(s) to be translated). - target (str): Language of the target document(s) (translation(s)). - words (int): Word count of the source document(s). - start (str): Project's start date in ISO format (YYYY-MM-DD). - deadline (str): Project's deadline in ISO format (YYYY-MM-DD). - price (float): Total price invoiced to the client (excl. VAT). - tm (bool): Whether a translation memory is available for this project. - domain (str): Overall domain to which the project belongs. - today (date): Date of the day where the script is run. - st (date): Project's start date, turned from an ISO-formatted string into a date. - dl (date): Project's deadline, turned from an ISO-formatted string into a date. - daysleft (timedelta): Time left until the project's deadline. - length (timedelta): Total time allocated to the project. - rate (float): Project's word rate. - efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek). - - Raises: - TypeError: If 'title' is not a string. - TypeError: If 'client' is not a string. - TypeError: If 'source' is not a string. - TypeError: If 'target' is not a string. - TypeError: If 'words' is not an integer. - TypeError: If 'start' is not a string. - ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits. - TypeError: If 'deadline' is not a string. - ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits. - TypeError: If 'price' is not a float. - TypeError: If 'tm' is not a boolean. - TypeError: If 'translator' is not a string. - TypeError: If 'revisor' is not a string. - TypeError: If 'status' is not a string. - ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed. - TypeError: If 'domain' is not a string. - """ - if type(title) != str: - raise TypeError("The title must be a string.") - else: - self.title = title - if type(client) != str: - raise TypeError("The client name must be a string.") - else: - self.client = client - if type(source) != str: - raise TypeError("The source language must be a string.") - else: - self.source = source - if type(target) != str: - raise TypeError("The target langague must be a string.") - else: - self.target = target - if type(words) != int: - raise TypeError("The word count must be an integer.") - else: - self.words = words - if type(start) != str: - raise TypeError("The start date must be provided as a string.") - elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", start): - raise ValueError("A valid start date must be provided in ISO format.") - else: - self.start = start - if type(deadline) != str: - raise TypeError("The deadline must be provided as a string.") - elif not re.match ("[0-9]{4}-[0-9]{2}-[0-9]{2}", deadline): - raise ValueError("A valid deadline must be provided in ISO format.") - else: - self.deadline = deadline - if type(price) != float: - raise TypeError("The price must be a float.") - else: - self.price = price - if type(tm) != bool: - raise TypeError("The TM availability must be a boolean.") - else: - self.tm = tm - if type(translator) != str: - raise TypeError("The translator's name must be a string.") - elif translator == '': - self.translator = "internal" - else: - self.translator = translator - if type(revisor) != str: - raise TypeError("The revisor's name must be a string.") - elif revisor == '': - self.revisor = "internal" - else: - self.revisor = revisor - if type(status) != str: - raise TypeError("The status must be a string.") - elif status == '': - self.status = "created" - elif not status.lower() in ["created", - "in translation", - "in revision", - "delivered", - "delayed", - "cancelled", - "canceled"]: - raise ValueError("Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.") - else: - self.status = status - if type(domain) != str: - raise TypeError("The domain must be a string.") - else: - self.domain = domain - - today = datetime.date.today() - self.st = datetime.date.fromisoformat(start) - self.dl = datetime.date.fromisoformat(deadline) - self.daysleft = self.dl - today - self.length = self.dl - self.st - self.rate = self.price/self.words - self.efficiency = words/((5/7)*self.length.days) - - def days_left(self): - """Displays how many days remain until the project deadline. - - Args: - None - - Returns: - str: A message informing the user that the deadline has been exceeded already if the deadline is in the past. - str: A message informing the user of the number of days left until the deadline if the deadline is not in the past. - """ - # prints a text indicating how many days are left until the project deadline - if self.dl < datetime.date.today(): - # if the deadline is in the past - return f"The deadline has been exceeded already." - else: - # if the deadline is not in the past - return f"There are {self.daysleft.days} days left until the deadline." - - def project_length(self): - """Displays the total number of days allocated to the project. - - Args: - None - - Returns: - str: The total number of days allocated to the project. - """ - return f"{self.length.days} days" - - def __str__(self): - # defines the print behaviour: returns a text providing the main information about the project - sent_1 = f"{self.title} is a translation for {self.client} from {self.source} into {self.target}." - if self.translator == "internal" and self.revisor == "internal": - sent_2 = f"Both the translator and the revisor are agency collaborators." - elif self.translator == "internal" and self.revisor != "internal": - sent_2 = f"The translator is an agency collaborator and the revisor is {self.revisor}." - elif self.translator != "internal" and self.revisor == "internal": - sent_2 = f"The translator is {self.translator} and the revisor is an agency collaborator." - else: - sent_2 = f"The translator is {self.translator} and the revisor is {self.revisor}." - # this if-statement considers whether a domain was added - if len(self.domain) > 0: - sent_3 = f"The domain is: {self.domain}." - else: - sent_3 = "The domain is unspecified." # if no domain was added, the text mentions it - sent_4 = f"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word." #the word rate is rounded to two decimal places to avoid cumbersomely long numbers - # this if-statement considers whether the deadline is in the past - if self.dl < datetime.date.today(): - sent_5 = f"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day." # the efficiency is rounded to units because you can't translate a fraction of a word anyway - else: - sent_5 = f"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day." - # this if-statement considers whether there is a translation memory for the project - sent_6 = f"There is {'a' if self.tm else 'no'} translation memory." - sent_7 = f"The project is currently {self.status}." - # print each sentence in a different line - return "\n".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7]) - -if __name__ == "__main": - parser = argparse.ArgumentParser( - "project", - description = "Read a project list and print the project information.", - epilog = "You get an overview of the agency's projects.") - parser.add_argument("filename", - type=argparse.FileType("r", encoding="utf-8"), - help="The list of translation projects") - args = parser.parse_args() - proj = Project(args.title, args.client, args.source, args.target, args.words, args.start, args.deadline, args.price, args.tm, args.translator, args.revisor, args.status, args.domain) - proj.days_left() - proj.project_length() - print(proj) - \ No newline at end of file From c9c4048a1f2b6c7f52eda8604f96888499aac55a Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:57:40 +0200 Subject: [PATCH 43/46] Deleting draft files --- script.py | 225 ------------------------------------------------------ 1 file changed, 225 deletions(-) delete mode 100644 script.py diff --git a/script.py b/script.py deleted file mode 100644 index 44dd874..0000000 --- a/script.py +++ /dev/null @@ -1,225 +0,0 @@ -#! /usr/bin/python - -import datetime -import re - -"""This script gives a translation agency an overview of all its projects. -""" -import argparse - -class Project: - - def __init__( - self, - title, - client, - source, - target, - words, - start, - deadline, - price, - tm, - translator = 'internal', - reviewer = 'internal', - status = 'created', - domain = ''): - """Initialises an object of the Project class, a class that represents a translation project of a translation agency. - - Args: - title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). - client (str): Client who ordered the translation. - source (str): Language of the source document(s) (document(s) to be translated). - target (str): Language of the target document(s) (translation(s)). - words (int): Word count of the source document(s). - start (str): Project's start date in ISO format (YYYY-MM-DD). - deadline (str): Project's deadline in ISO format (YYYY-MM-DD). - price (float): Total price invoiced to the client (excl. VAT). - tm (bool): Whether a translation memory is available for this project. - translator (string, optional): Translator assigned to the project. Defaults to 'internal'. - reviewer (string, optional): Reviewer assigned to the project. Defaults to 'internal'. - status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'. - domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string. - - Attributes: - title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). - client (str): Client who ordered the translation. - source (str): Language of the source document(s) (document(s) to be translated). - target (str): Language of the target document(s) (translation(s)). - words (int): Word count of the source document(s). - start (str): Project's start date in ISO format (YYYY-MM-DD). - deadline (str): Project's deadline in ISO format (YYYY-MM-DD). - price (float): Total price invoiced to the client (excl. VAT). - tm (bool): Whether a translation memory is available for this project. - translator (string, optional): Translator assigned to the project. - reviewer (string, optional): Reviewer assigned to the project. - status (string, optional): Current project status inside the agency's workflow. - domain (str): Overall domain to which the project belongs. - today (date): Date of the day where the script is run. - st (date): Project's start date, turned from an ISO-formatted string into a date. - dl (date): Project's deadline, turned from an ISO-formatted string into a date. - daysleft (timedelta): Time left until the project's deadline. - length (timedelta): Total time allocated to the project. - rate (float): Project's word rate. - efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek). - - Raises: - TypeError: If 'title' is not a string. - TypeError: If 'client' is not a string. - TypeError: If 'source' is not a string. - TypeError: If 'target' is not a string. - TypeError: If 'words' is not an integer. - TypeError: If 'start' is not a string. - ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits. - TypeError: If 'deadline' is not a string. - ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits. - TypeError: If 'price' is not a float. - TypeError: If 'tm' is not a boolean. - TypeError: If 'translator' is not a string. - TypeError: If 'reviewer' is not a string. - TypeError: If 'status' is not a string. - ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed. - TypeError: If 'domain' is not a string. - """ - if type(title) != str: - raise TypeError("The title must be a string.") - else: - self.title = title - if type(client) != str: - raise TypeError("The client name must be a string.") - else: - self.client = client - if type(source) != str: - raise TypeError("The source language must be a string.") - else: - self.source = source - if type(target) != str: - raise TypeError("The target langague must be a string.") - else: - self.target = target - if type(words) != int: - raise TypeError("The word count must be an integer.") - else: - self.words = words - if type(start) != str: - raise TypeError("The start date must be provided as a string.") - try: - self.st = datetime.date.fromisoformat(start) - except: - raise TypeError("The start date must be provided in ISOFormat") - else: - self.start = start - if type(deadline) != str: - raise TypeError("The deadline must be provided as a string.") - try: - self.dl = datetime.date.fromisoformat(deadline) - except: - raise TypeError("The start deadline must be provided in ISOFormat") - else: - self.deadline = deadline - if type(price) != float: - raise TypeError("The price must be a float.") - else: - self.price = price - if type(tm) != bool: - raise TypeError("The TM availability must be a boolean.") - else: - self.tm = tm - if type(translator) != str: - raise TypeError("The translator's name must be a string.") - elif translator == '': - self.translator = "internal" - else: - self.translator = translator - if type(reviewer) != str: - raise TypeError("The reviewer's name must be a string.") - elif reviewer == '': - self.reviewer = "internal" - else: - self.reviewer = reviewer - if type(status) != str: - raise TypeError("The status must be a string.") - elif status == '': - self.status = "created" - elif not status.lower() in ["created", - "in translation", - "in revision", - "delivered", - "delayed", - "cancelled", - "canceled"]: - raise ValueError("Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.") - else: - self.status = status - if type(domain) != str: - raise TypeError("The domain must be a string.") - else: - self.domain = domain - - today = datetime.date.today() - self.daysleft = self.dl - today - self.length = self.dl - self.st - self.rate = self.price/self.words - self.efficiency = words/((5/7)*self.length.days) - - def days_left(self): - """Displays how many days remain until the project deadline. - - Args: - None - - Returns: - str: A message informing the user that the deadline has been exceeded already if the deadline is in the past. - str: A message informing the user of the number of days left until the deadline if the deadline is not in the past. - """ - # prints a text indicating how many days are left until the project deadline - if self.dl < datetime.date.today(): - # if the deadline is in the past - return f"The deadline has been exceeded already." - else: - # if the deadline is not in the past - return f"There are {self.daysleft.days} days left until the deadline." - - def project_length(self): - """Displays the total number of days allocated to the project. - - Args: - None - - Returns: - str: The total number of days allocated to the project. - """ - return f"{self.length.days} days" - - def __str__(self): - # defines the print behaviour: returns a text providing the main information about the project - sent_1 = f"{self.title} is a translation for {self.client} from {self.source} into {self.target}." - if self.translator.lower() == "internal" and self.reviewer.lower() == "internal": - sent_2 = f"Both the translator and the reviewer are agency collaborators." - elif self.translator.lower() == "internal" and self.reviewer.lower() != "internal": - sent_2 = f"The translator is an agency collaborator and the reviewer is {self.reviewer}." - elif self.translator.lower() != "internal" and self.reviewer.lower() == "internal": - sent_2 = f"The translator is {self.translator} and the reviewer is an agency collaborator." - else: - sent_2 = f"The translator is {self.translator} and the reviewer is {self.reviewer}." - # this if-statement considers whether a domain was added - if len(self.domain) > 0: - sent_3 = f"The domain is: {self.domain}." - else: - sent_3 = "The domain is unspecified." # if no domain was added, the text mentions it - sent_4 = f"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word." #the word rate is rounded to two decimal places to avoid cumbersomely long numbers - # this if-statement considers whether the deadline is in the past - if self.dl < datetime.date.today(): - sent_5 = f"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day." # the efficiency is rounded to units because you can't translate a fraction of a word anyway - else: - sent_5 = f"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day." - # this if-statement considers whether there is a translation memory for the project - sent_6 = f"There is {'a' if self.tm else 'no'} translation memory." - sent_7 = f"The project is currently {self.status}." - # print each sentence in a different line - return "\n".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7]) - -if __name__ == "__main": - # write here what happens if the code is run instead of imported - # include argparse code! - \ No newline at end of file From d062efdb7ed66fdd5f4bdd3a33244c09501d2cf5 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:57:52 +0200 Subject: [PATCH 44/46] Deleting draft files --- tutorial.md | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 tutorial.md diff --git a/tutorial.md b/tutorial.md deleted file mode 100644 index 24a2674..0000000 --- a/tutorial.md +++ /dev/null @@ -1,4 +0,0 @@ -# How to use my script - -In this notebook you will learn how to use the functions and classes defined in `script.py`. -This will become a tutorial \ No newline at end of file From f0d258bef1c9112b12f803252138b224e5d68509 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 10:59:40 +0200 Subject: [PATCH 45/46] Uploading final files These are the final files for the last assignment of Introduction to Python. --- README.ipynb | 193 +++ README.md | 107 ++ add-on_TM-generator.ipynb | 270 ++++ add-on_TM-generator.md | 148 +++ add-on_database-generator.ipynb | 288 ++++ add-on_database-generator.md | 184 +++ freelancers_db.json | 1 + guide_bruxelles.json | 1 + handboek.json | 1 + project-management-tool_basic.py | 244 ++++ project-management-tool_expanded.ipynb | 1683 ++++++++++++++++++++++++ project-management-tool_expanded.md | 1186 +++++++++++++++++ projects_db.json | 1 + python_en.txt | 1 + python_fr.txt | 1 + rhumatismes_inflammatoires.json | 1 + user_guide.json | 1 + 17 files changed, 4311 insertions(+) create mode 100644 README.ipynb create mode 100644 README.md create mode 100644 add-on_TM-generator.ipynb create mode 100644 add-on_TM-generator.md create mode 100644 add-on_database-generator.ipynb create mode 100644 add-on_database-generator.md create mode 100644 freelancers_db.json create mode 100644 guide_bruxelles.json create mode 100644 handboek.json create mode 100644 project-management-tool_basic.py create mode 100644 project-management-tool_expanded.ipynb create mode 100644 project-management-tool_expanded.md create mode 100644 projects_db.json create mode 100644 python_en.txt create mode 100644 python_fr.txt create mode 100644 rhumatismes_inflammatoires.json create mode 100644 user_guide.json diff --git a/README.ipynb b/README.ipynb new file mode 100644 index 0000000..253343c --- /dev/null +++ b/README.ipynb @@ -0,0 +1,193 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0b6e787b", + "metadata": {}, + "source": [ + "# Project management tool for a translation agency" + ] + }, + { + "cell_type": "markdown", + "id": "13819178", + "metadata": {}, + "source": [ + "## Introduction\n", + "Every translation agency needs to keep track of its past and ongoing projects, just like any company. However, when projects start to accumulate, it may become rather difficult to keep an overview. In addition, large tables or endless database entries do not make for comfortable reading. This tool enables project managers to quickly get an overview of their translation projects and all their information, as a user-friendly and informative text, as well as retrieving specific information." + ] + }, + { + "cell_type": "markdown", + "id": "57f1c0ca", + "metadata": {}, + "source": [ + "## Files in this repository\n", + "- `README.md` (this file): Gives an overview of the repository, the files it contains and how to use them.\n", + "- `project-management-tool_basic.py`: A basic version of the project management tool, which can be run from the command line rather than importing it to use it.\n", + "- `project-management-tool_expanded.ipynb`: An interactive version of the script, with more features than the basic version, but which needs to be imported and opened and cannot be run from the command line.\n", + "- `project-management-tool_expanded.md`: The markdown version of the file above.\n", + "- `projects_db.json`: A sample project database, containing a list of dictionaries with all the information for each project, namely:\n", + " - `title` (a string) indicates the project's title (typically the title of the source document, or the overall title the translator gave the project if there's more than one document to be translated);\n", + " - `client` (a string) indicates the client who ordered the translation;\n", + " - `source` (a string) indicates the language of the source document (document to be translated);\n", + " - `target` (a string) indicates the language of the target document (translation);\n", + " - `words` (an integer) indicates the word count of the source document;\n", + " - `start` (a string) indicates the project's start date in ISO format (YYYY-MM-DD);\n", + " - `deadline` (a string) indicates the project's deadline in ISO format (YYYY-MM-DD);\n", + " - `price` (a float) indicates the total price invoiced to the client (excl. VAT);\n", + " - `tm` (a boolean) indicates whether or not a translation memory is available for this project;\n", + " - `translator` (a string) indicates the freelancer assigned to translate this project (using their unique reference inside the agency's database) or is left empty to indicate that an agency collaborator translated the project;\n", + " - `reviewer` (a string) indicates the freelancer assigned to review this project (using their unique reference inside the agency's database) or is left empty to indicate that an agency collaborator reviewed the project;\n", + " - `status` (a string) indicates the project's status in the agency workflow;\n", + " - `domain` (a string) indicates the overall domain to which the project belongs.\n", + "- `guide_bruxelles.json`, `handboek.json`, `rhumatismes_inflammatoires.json` and `user_guide.json`: Separate files containing the same information as `projects_db.json`, but with one file per project.\n", + "- `freelancers_db.json`: A sample database (again, as a list of dictionaries), with an overview of three freelancers' information, namely:\n", + " - `name` (a string): The freelancer's name in Firstname Lastname format;\n", + " - `email`(a string): The freelancer's email address;\n", + " - `phone`(a string): The freelancer's phone number (international version, so starting with \"+\");\n", + " - `task` (a set of strings): The task(s) the freelancer usually performs for the agency, namely whether they're a translator or a reviewer;\n", + " - `language`(a set of strings): The freelancer's working language(s). \n", + "- `add-on_database-generator.ipynb` and `add-on_database-generator.md`: An add-on not integrated in the project management tool itself, but which you can use to generate the freelancer/project information in the dictionary format you may need for the script (i.e. same format as the json-files mentioned above).\n", + "- `add-on_TM-generator.ipynb` and `add-on_TM-generator.md`: An add-on not integrated in the project management tool itself, but which you can use to generate a translation memory based on a source and target text, so you can turn your project information from `TM = false` into `TM = true`.\n", + "- `python_en.txt` and `python_en.txt`: A sample source and target text to try out the TM generator." + ] + }, + { + "cell_type": "markdown", + "id": "c103f3f4", + "metadata": {}, + "source": [ + "## Composition of the project management tool" + ] + }, + { + "cell_type": "markdown", + "id": "eb04b1a9", + "metadata": {}, + "source": [ + "### Basic version\n", + "The tool consists of a Python class called `Project`. That class creates objects with the following **attributes**:\n", + "- 13 instance attributes, which are the same as dictionary keys in the project information dictionaries. The last four attributes are optional and have the following default values:\n", + " - `translator`: \"internal\", meaning that an agency collaborator was assigned to the project in this role;\n", + " - `reviewer`: \"internal\", meaning that an agency collaborator was assigned to the project in this role;\n", + " - `status`: \"created\";\n", + " - `domain`: An empty string, meaning that no domain was provided for this project (so it's probably a general text).\n", + "- 7 computed attributes, namely:\n", + " - `st`: A conversion of the start date from a string into an ISO-formatted date using the `datetime` module.\n", + " - `dl`: A conversion of the deadline from a string into an ISO-formatted date using the `datetime` module.\n", + " - `today`: The date of the day on which the tool is used, computed using the `datetime` module.\n", + " - `daysleft`: The number of days left until the project deadline, computed by substracting the current date (`today`) from the project deadline (`dl`).\n", + " - `length`: The total number of days foreseen for the project (weekends and holidays included), computed by subtracting the start date (`st`) from the deadline (`dl`).\n", + " - `rate`: The rate per word the client pays for the project, computed by dividing the project price by the word count.\n", + " - `efficiency`: The number of words to translate or revise per work day to meet the deadline, computed by dividing the word count by 5/7th of the total project length. Does not take into account holidays.\n", + "\n", + "The class also has three **methods**:\n", + "- `days_left`: Prints a message indicating the number of days left until the project deadline if the deadline is in the past or present, and indicating that the deadline has been exceeded already if it is in the past.\n", + "- `project_length`: Prints the total number of days (weekends included) foreseen for the project (as calling that attribute would otherwise return a datetime timedelta, which isn't very clear for most users).\n", + "- a `printing method`, which displays the project information using the following text template:\n", + " - \"`title` is a translation for`client` from `source` into `target`.\n", + " - Both the translator and the reviewer are agency collaborators. (Or \"The translator is `translator` and the reviewer is `reviewer`.\" if they're freelancers.)\n", + " - The domain is `domain`(or \"unspecified\" if no domain was provided).\n", + " - It's `words` words long, with a rate of `rate` € per word.\n", + " - It started on `st` and is due on `dl`, so `length` days are foreseen for it, of which `daysleft` left. To meet the deadline,`efficiency` words need to be translated or revised per day.\n", + " - There is a translation memory. (Or \"no\" if there's none.)\n", + " - The project is currently `status`.\"\n", + "\n", + "The script is documented with **docstrings** and has an **argparse parser**, so it can be run directly from the command line." + ] + }, + { + "cell_type": "markdown", + "id": "7b995db0", + "metadata": {}, + "source": [ + "### Expanded version\n", + "The expanded version of the script has all the features the basic version has, except for the argparse parser (due to time constraints). In addition, it has some extra features that the basic version does not have.\n", + "\n", + "Next to the `Project` class, the expanded tool has a `Freelancer` class to turn the entries of the freelancer database found in `freelancers_db.json` into objects. The **attributes** of that class are the same as the keys in that list of dictionaries. The class also has two **methods**, namely:\n", + "- A method to import arguments using keyword arguments (`kwargs`), rather than manually inputting each attribute;\n", + "- A `printing method`, which displays the freelancer information using the following text template:\n", + " - \"`name` can be contacted via e-mail (`email`) or via phone (`phone`).\n", + " - `name`'s reference in our database is `ref`.\n", + " - `name` works as a `task` for `language`.\"\n", + "\n", + "Due to time constraints, this class is **not documented with docstrings**.\n", + "\n", + "This added `Freelancer` class allows project managers to not only get an overview of their project's information, but to link the project database and the freelancer database to easily look up information about the freelancers assigned to a project (say, if they're late to deliver or something changes in the project organisation and the PM wants to contact them via e-mail)." + ] + }, + { + "cell_type": "markdown", + "id": "840d6e0d", + "metadata": {}, + "source": [ + "## How to use the project management tool" + ] + }, + { + "cell_type": "markdown", + "id": "d66823a8", + "metadata": {}, + "source": [ + "### Basic version\n", + "You can run the basic version of the script directly from the command line and manually feed it the project information, using:\n", + "\n", + "`python project-management-tool_basic.py \"title\" \"client\" \"source\" \"target\" words \"YYYY-MM-DD\" \"YYYY-MM-DD\" price tm`\n", + "\n", + "(The placeholders need to be replaced by the actual project information, check above that you input all the information as the right variable type, otherwise you will get an error message asking you to use the correct type.)" + ] + }, + { + "cell_type": "markdown", + "id": "cbe342bd", + "metadata": {}, + "source": [ + "### Expanded version\n", + "Because no argparse parser was programmed yet for this version of the tool, it needs to be opened rather than being run from the command line. You can open `project-management-tool_expanded.ipynb` with Jupyter Notebooks, or `project-management-tool_expanded.md` with, for example, VS Code. For more information about how to use these files, open them and go through the tutorial in them. Unlike with the basic version run from the command line, this version of the tool allows files as input to instantiate objects. It is possible to print the information of all the projects in one go, using `projects_db.json`, or to link the `Freelancer` and `Project` class for a single project, using `freelancers_db.json` and one of the separate project files." + ] + }, + { + "cell_type": "markdown", + "id": "804365fe", + "metadata": {}, + "source": [ + "## Future features\n", + "Due to time constraints, several features could not be implemented, which will be added in future updates:\n", + "- An argparse parser for the expanded version, which will enable the user to run it from the command line rather than having to open the script. Said parser will also work with files, rather than forcing the user to manually input every attribute, which will result in a significant time gain and make use of the tool easier.\n", + "- Validation and docstrings in the `Freelancer` class.\n", + "- Validation of the project deadline: ensure that the deadline comes *after* the start date, not before.\n", + "- Fine-tuning the `start` date and `deadline`, so the PM can either specify at what time of the day the project starts/needs to be delivered, or not specify it (in which case, the default start/end time is midnight for the start and 11:59 pm for the deadline).\n", + "- Differentiate between a rush assignment (`efficiency` calculated on 7/7 of the project lenth instead of 5/7) and a regular assignment (`efficiency` calculated on 5/7 of the project length).\n", + "- Fine-tuning of the `efficiency` by taking into account weekends and holidays (ex. so a regular four-days project is counted as four full work days if it spans from Monday to Thursday, but only two full work days if it spans from Friday to Monday, rather than calculating 5/7 of the full project length in both cases).\n", + "- A `Client` class with all the necessary client information (contact, VAT-number, bank account number, domain, unique reference inside the agency database...) This class will be linked to the `Project` class just like `Freelancer`, facilitating retrieval of client information.\n", + "- The possibility to loop through all the separate project files even when linking `Project` and `Freelancer` objects, so you don't have to run the script for each project separately (which is feasible for four projects, but far too time-consuming in a real agency setting).\n", + "- An extension of the recognised file formats, from json or csv to xml, excel-documents...\n", + "- Fine-tuning the `efficiency` attribute by creating a specific attribute for translation and for review (for example 2/3 of the time for translation and 1/3 for review).\n", + "- Fine-tuning the `rate` and `price` attributes by linking the rate to a `rate` entry in the freelancer database (since each freelancer has their own rates, and rate is not applicable to agency collaborators, who receive a fixed salary per month rather than being paid per project) and calculating the agency's margin after substraction of the freelancer fees from the total project price.\n", + "- Ensuring standardisation of the dates in a certain time zone, to avoid calculation errors if the start date and deadline were fixed in a different time zone from that of the person using the script (since their time zone determines the computation of \"today\")." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..03e1eab --- /dev/null +++ b/README.md @@ -0,0 +1,107 @@ +# Project management tool for a translation agency + +## Introduction +Every translation agency needs to keep track of its past and ongoing projects, just like any company. However, when projects start to accumulate, it may become rather difficult to keep an overview. In addition, large tables or endless database entries do not make for comfortable reading. This tool enables project managers to quickly get an overview of their translation projects and all their information, as a user-friendly and informative text, as well as retrieving specific information. + +## Files in this repository +- `README.md` (this file): Gives an overview of the repository, the files it contains and how to use them. +- `project-management-tool_basic.py`: A basic version of the project management tool, which can be run from the command line rather than importing it to use it. +- `project-management-tool_expanded.ipynb`: An interactive version of the script, with more features than the basic version, but which needs to be imported and opened and cannot be run from the command line. +- `project-management-tool_expanded.md`: The markdown version of the file above. +- `projects_db.json`: A sample project database, containing a list of dictionaries with all the information for each project, namely: + - `title` (a string) indicates the project's title (typically the title of the source document, or the overall title the translator gave the project if there's more than one document to be translated); + - `client` (a string) indicates the client who ordered the translation; + - `source` (a string) indicates the language of the source document (document to be translated); + - `target` (a string) indicates the language of the target document (translation); + - `words` (an integer) indicates the word count of the source document; + - `start` (a string) indicates the project's start date in ISO format (YYYY-MM-DD); + - `deadline` (a string) indicates the project's deadline in ISO format (YYYY-MM-DD); + - `price` (a float) indicates the total price invoiced to the client (excl. VAT); + - `tm` (a boolean) indicates whether or not a translation memory is available for this project; + - `translator` (a string) indicates the freelancer assigned to translate this project (using their unique reference inside the agency's database) or is left empty to indicate that an agency collaborator translated the project; + - `reviewer` (a string) indicates the freelancer assigned to review this project (using their unique reference inside the agency's database) or is left empty to indicate that an agency collaborator reviewed the project; + - `status` (a string) indicates the project's status in the agency workflow; + - `domain` (a string) indicates the overall domain to which the project belongs. +- `guide_bruxelles.json`, `handboek.json`, `rhumatismes_inflammatoires.json` and `user_guide.json`: Separate files containing the same information as `projects_db.json`, but with one file per project. +- `freelancers_db.json`: A sample database (again, as a list of dictionaries), with an overview of three freelancers' information, namely: + - `name` (a string): The freelancer's name in Firstname Lastname format; + - `email`(a string): The freelancer's email address; + - `phone`(a string): The freelancer's phone number (international version, so starting with "+"); + - `task` (a set of strings): The task(s) the freelancer usually performs for the agency, namely whether they're a translator or a reviewer; + - `language`(a set of strings): The freelancer's working language(s). +- `add-on_database-generator.ipynb` and `add-on_database-generator.md`: An add-on not integrated in the project management tool itself, but which you can use to generate the freelancer/project information in the dictionary format you may need for the script (i.e. same format as the json-files mentioned above). +- `add-on_TM-generator.ipynb` and `add-on_TM-generator.md`: An add-on not integrated in the project management tool itself, but which you can use to generate a translation memory based on a source and target text, so you can turn your project information from `TM = false` into `TM = true`. +- `python_en.txt` and `python_en.txt`: A sample source and target text to try out the TM generator. + +## Composition of the project management tool + +### Basic version +The tool consists of a Python class called `Project`. That class creates objects with the following **attributes**: +- 13 instance attributes, which are the same as dictionary keys in the project information dictionaries. The last four attributes are optional and have the following default values: + - `translator`: "internal", meaning that an agency collaborator was assigned to the project in this role; + - `reviewer`: "internal", meaning that an agency collaborator was assigned to the project in this role; + - `status`: "created"; + - `domain`: An empty string, meaning that no domain was provided for this project (so it's probably a general text). +- 7 computed attributes, namely: + - `st`: A conversion of the start date from a string into an ISO-formatted date using the `datetime` module. + - `dl`: A conversion of the deadline from a string into an ISO-formatted date using the `datetime` module. + - `today`: The date of the day on which the tool is used, computed using the `datetime` module. + - `daysleft`: The number of days left until the project deadline, computed by substracting the current date (`today`) from the project deadline (`dl`). + - `length`: The total number of days foreseen for the project (weekends and holidays included), computed by subtracting the start date (`st`) from the deadline (`dl`). + - `rate`: The rate per word the client pays for the project, computed by dividing the project price by the word count. + - `efficiency`: The number of words to translate or revise per work day to meet the deadline, computed by dividing the word count by 5/7th of the total project length. Does not take into account holidays. + +The class also has three **methods**: +- `days_left`: Prints a message indicating the number of days left until the project deadline if the deadline is in the past or present, and indicating that the deadline has been exceeded already if it is in the past. +- `project_length`: Prints the total number of days (weekends included) foreseen for the project (as calling that attribute would otherwise return a datetime timedelta, which isn't very clear for most users). +- a `printing method`, which displays the project information using the following text template: + - "`title` is a translation for`client` from `source` into `target`. + - Both the translator and the reviewer are agency collaborators. (Or "The translator is `translator` and the reviewer is `reviewer`." if they're freelancers.) + - The domain is `domain`(or "unspecified" if no domain was provided). + - It's `words` words long, with a rate of `rate` € per word. + - It started on `st` and is due on `dl`, so `length` days are foreseen for it, of which `daysleft` left. To meet the deadline,`efficiency` words need to be translated or revised per day. + - There is a translation memory. (Or "no" if there's none.) + - The project is currently `status`." + +The script is documented with **docstrings** and has an **argparse parser**, so it can be run directly from the command line. + +### Expanded version +The expanded version of the script has all the features the basic version has, except for the argparse parser (due to time constraints). In addition, it has some extra features that the basic version does not have. + +Next to the `Project` class, the expanded tool has a `Freelancer` class to turn the entries of the freelancer database found in `freelancers_db.json` into objects. The **attributes** of that class are the same as the keys in that list of dictionaries. The class also has two **methods**, namely: +- A method to import arguments using keyword arguments (`kwargs`), rather than manually inputting each attribute; +- A `printing method`, which displays the freelancer information using the following text template: + - "`name` can be contacted via e-mail (`email`) or via phone (`phone`). + - `name`'s reference in our database is `ref`. + - `name` works as a `task` for `language`." + +Due to time constraints, this class is **not documented with docstrings**. + +This added `Freelancer` class allows project managers to not only get an overview of their project's information, but to link the project database and the freelancer database to easily look up information about the freelancers assigned to a project (say, if they're late to deliver or something changes in the project organisation and the PM wants to contact them via e-mail). + +## How to use the project management tool + +### Basic version +You can run the basic version of the script directly from the command line and manually feed it the project information, using: + +`python project-management-tool_basic.py "title" "client" "source" "target" words "YYYY-MM-DD" "YYYY-MM-DD" price tm` + +(The placeholders need to be replaced by the actual project information, check above that you input all the information as the right variable type, otherwise you will get an error message asking you to use the correct type.) + +### Expanded version +Because no argparse parser was programmed yet for this version of the tool, it needs to be opened rather than being run from the command line. You can open `project-management-tool_expanded.ipynb` with Jupyter Notebooks, or `project-management-tool_expanded.md` with, for example, VS Code. For more information about how to use these files, open them and go through the tutorial in them. Unlike with the basic version run from the command line, this version of the tool allows files as input to instantiate objects. It is possible to print the information of all the projects in one go, using `projects_db.json`, or to link the `Freelancer` and `Project` class for a single project, using `freelancers_db.json` and one of the separate project files. + +## Future features +Due to time constraints, several features could not be implemented, which will be added in future updates: +- An argparse parser for the expanded version, which will enable the user to run it from the command line rather than having to open the script. Said parser will also work with files, rather than forcing the user to manually input every attribute, which will result in a significant time gain and make use of the tool easier. +- Validation and docstrings in the `Freelancer` class. +- Validation of the project deadline: ensure that the deadline comes *after* the start date, not before. +- Fine-tuning the `start` date and `deadline`, so the PM can either specify at what time of the day the project starts/needs to be delivered, or not specify it (in which case, the default start/end time is midnight for the start and 11:59 pm for the deadline). +- Differentiate between a rush assignment (`efficiency` calculated on 7/7 of the project lenth instead of 5/7) and a regular assignment (`efficiency` calculated on 5/7 of the project length). +- Fine-tuning of the `efficiency` by taking into account weekends and holidays (ex. so a regular four-days project is counted as four full work days if it spans from Monday to Thursday, but only two full work days if it spans from Friday to Monday, rather than calculating 5/7 of the full project length in both cases). +- A `Client` class with all the necessary client information (contact, VAT-number, bank account number, domain, unique reference inside the agency database...) This class will be linked to the `Project` class just like `Freelancer`, facilitating retrieval of client information. +- The possibility to loop through all the separate project files even when linking `Project` and `Freelancer` objects, so you don't have to run the script for each project separately (which is feasible for four projects, but far too time-consuming in a real agency setting). +- An extension of the recognised file formats, from json or csv to xml, excel-documents... +- Fine-tuning the `efficiency` attribute by creating a specific attribute for translation and for review (for example 2/3 of the time for translation and 1/3 for review). +- Fine-tuning the `rate` and `price` attributes by linking the rate to a `rate` entry in the freelancer database (since each freelancer has their own rates, and rate is not applicable to agency collaborators, who receive a fixed salary per month rather than being paid per project) and calculating the agency's margin after substraction of the freelancer fees from the total project price. +- Ensuring standardisation of the dates in a certain time zone, to avoid calculation errors if the start date and deadline were fixed in a different time zone from that of the person using the script (since their time zone determines the computation of "today"). diff --git a/add-on_TM-generator.ipynb b/add-on_TM-generator.ipynb new file mode 100644 index 0000000..6369480 --- /dev/null +++ b/add-on_TM-generator.ipynb @@ -0,0 +1,270 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "45eba7f4", + "metadata": {}, + "source": [ + "# Source and target text aligner\n", + "Sometimes, you still have some translations left over from a time where you didn't use CAT-tools and you'd like to feed them into your translation memory. Some CAT-tools have built-in text aligners, but not all of them, so how do you go from two separate text documents to an aligned bilingual (csv-)file ready to be fed into your TM?" + ] + }, + { + "cell_type": "markdown", + "id": "cd681b03", + "metadata": {}, + "source": [ + "## Step one: Prepare the source and target text\n", + "The easiest file format to start from is a pure txt-file... and since for a TM only the pure text is of interest, converting a Word-, PowerPoint- or whatever file to a txt-file isn't an issue. So, we'll take the original source and target document and export them to a txt-format (with utf-8 encoding).\n", + "## Step two: Store the two (continuous) texts into variables" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "4ccbdff0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Introduction to Machine Learning with Python.\\n\\nThis module provides an introduction to the basic concepts and use of the Python programming language in support of translation. Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.\\n'" + ] + }, + "execution_count": 1, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "with open('python_en.txt', encoding = 'utf-8') as f:\n", + " st_1 = f.read()\n", + "st_1" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "01e779fd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Introduction au machine learning à l’aide de Python.\\n\\nCe module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.\\n'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "with open('python_fr.txt', encoding = 'utf-8') as f:\n", + " tt_1 = f.read()\n", + "tt_1" + ] + }, + { + "cell_type": "markdown", + "id": "7c32d752", + "metadata": {}, + "source": [ + "## Step three: Split the single text string into list of sentences\n", + "Since most TMs (and CAT-tools) use sentence segmentation, the source and target text need to be split up into sentences. So, each text becomes a list of separate sentences.\n", + "\n", + "For this, we use `nltk tokenizer`, which functions with English and French (and many other languages, but English and French are the ones that interest us right now)." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "fa4734fa", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Introduction to Machine Learning with Python.',\n", + " 'This module provides an introduction to the basic concepts and use of the Python programming language in support of translation.',\n", + " 'Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.']" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from nltk.tokenize import sent_tokenize\n", + "split_st_1 = sent_tokenize(st_1, language = 'english')\n", + "split_st_1" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "afdef22f", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['Introduction au machine learning à l’aide de Python.',\n", + " 'Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction.',\n", + " 'L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.']" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from nltk.tokenize import sent_tokenize\n", + "split_tt_1 = sent_tokenize(tt_1, language = 'french')\n", + "split_tt_1" + ] + }, + { + "cell_type": "markdown", + "id": "a554c888", + "metadata": {}, + "source": [ + "## Step four: Aligning those lists and exporting the tuples list to a csv-file\n", + "Writing a csv-file looks fairly similar to reading a txt-file, like we did at the start of the process, except this time we use `f.write` instead of `f.read`.\n", + "A csv-file consists of rows, often a first header row with the label of each column, followed by the actual content of the file. Both are defined separately.\n", + "- The content of the header row (defined in the first indented line) simply consists of the language codes of the source and target language.\n", + "- The content of the next rows (defined in the next indented lines) contains our texts.\n", + " - The `zip()`-function aligns the first sentence of the source text with the first sentence of the target text, the second with the second... and so on in x, y tuples.\n", + " - Next, those tuples are put inside an f-string that separates x and y with a tab and places makes a new line start after each y." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "c07d868f", + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"translation_memory.csv\", \"w\", encoding = \"utf-8\") as f: # open file to overwrite\n", + " f.write(\"EN\\tFR\\n\") # write heading\n", + " for x, y in zip(split_st_1, split_tt_1): # go through each pair of lines\n", + " f.write(f\"{x}\\t{y}\\n\") # write the line" + ] + }, + { + "cell_type": "markdown", + "id": "b3ffb77f", + "metadata": {}, + "source": [ + "## Step five: Admiring our work\n", + "Using pandas, we can read the newly created csv-file." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f1a8de6c", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "2f5e0b81", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
ENFR
0Introduction to Machine Learning with Python.Introduction au machine learning à l’aide de P...
1This module provides an introduction to the ba...Ce module offre une introduction aux concepts ...
2Focus lies on the main concepts that include N...L’accent est mis sur le traitement du langage ...
\n", + "
" + ], + "text/plain": [ + " EN \\\n", + "0 Introduction to Machine Learning with Python. \n", + "1 This module provides an introduction to the ba... \n", + "2 Focus lies on the main concepts that include N... \n", + "\n", + " FR \n", + "0 Introduction au machine learning à l’aide de P... \n", + "1 Ce module offre une introduction aux concepts ... \n", + "2 L’accent est mis sur le traitement du langage ... " + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "read_tm = pd.read_csv('translation_memory.csv', sep = '\\t')\n", + "read_tm.head()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/add-on_TM-generator.md b/add-on_TM-generator.md new file mode 100644 index 0000000..f69b006 --- /dev/null +++ b/add-on_TM-generator.md @@ -0,0 +1,148 @@ +# Source and target text aligner +Sometimes, you still have some translations left over from a time where you didn't use CAT-tools and you'd like to feed them into your translation memory. Some CAT-tools have built-in text aligners, but not all of them, so how do you go from two separate text documents to an aligned bilingual (csv-)file ready to be fed into your TM? + +## Step one: Prepare the source and target text +The easiest file format to start from is a pure txt-file... and since for a TM only the pure text is of interest, converting a Word-, PowerPoint- or whatever file to a txt-file isn't an issue. So, we'll take the original source and target document and export them to a txt-format (with utf-8 encoding). +## Step two: Store the two (continuous) texts into variables + + +```python +with open('python_en.txt', encoding = 'utf-8') as f: + st_1 = f.read() +st_1 +``` + + + + + 'Introduction to Machine Learning with Python.\n\nThis module provides an introduction to the basic concepts and use of the Python programming language in support of translation. Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.\n' + + + + +```python +with open('python_fr.txt', encoding = 'utf-8') as f: + tt_1 = f.read() +tt_1 +``` + + + + + 'Introduction au machine learning à l’aide de Python.\n\nCe module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.\n' + + + +## Step three: Split the single text string into list of sentences +Since most TMs (and CAT-tools) use sentence segmentation, the source and target text need to be split up into sentences. So, each text becomes a list of separate sentences. + +For this, we use `nltk tokenizer`, which functions with English and French (and many other languages, but English and French are the ones that interest us right now). + + +```python +from nltk.tokenize import sent_tokenize +split_st_1 = sent_tokenize(st_1, language = 'english') +split_st_1 +``` + + + + + ['Introduction to Machine Learning with Python.', + 'This module provides an introduction to the basic concepts and use of the Python programming language in support of translation.', + 'Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning.'] + + + + +```python +from nltk.tokenize import sent_tokenize +split_tt_1 = sent_tokenize(tt_1, language = 'french') +split_tt_1 +``` + + + + + ['Introduction au machine learning à l’aide de Python.', + 'Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction.', + 'L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning.'] + + + +## Step four: Aligning those lists and exporting the tuples list to a csv-file +Writing a csv-file looks fairly similar to reading a txt-file, like we did at the start of the process, except this time we use `f.write` instead of `f.read`. +A csv-file consists of rows, often a first header row with the label of each column, followed by the actual content of the file. Both are defined separately. +- The content of the header row (defined in the first indented line) simply consists of the language codes of the source and target language. +- The content of the next rows (defined in the next indented lines) contains our texts. + - The `zip()`-function aligns the first sentence of the source text with the first sentence of the target text, the second with the second... and so on in x, y tuples. + - Next, those tuples are put inside an f-string that separates x and y with a tab and places makes a new line start after each y. + + +```python +with open("translation_memory.csv", "w", encoding = "utf-8") as f: # open file to overwrite + f.write("EN\tFR\n") # write heading + for x, y in zip(split_st_1, split_tt_1): # go through each pair of lines + f.write(f"{x}\t{y}\n") # write the line +``` + +## Step five: Admiring our work +Using pandas, we can read the newly created csv-file. + + +```python +import pandas as pd +``` + + +```python +read_tm = pd.read_csv('translation_memory.csv', sep = '\t') +read_tm.head() +``` + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
ENFR
0Introduction to Machine Learning with Python.Introduction au machine learning à l’aide de P...
1This module provides an introduction to the ba...Ce module offre une introduction aux concepts ...
2Focus lies on the main concepts that include N...L’accent est mis sur le traitement du langage ...
+
+ + diff --git a/add-on_database-generator.ipynb b/add-on_database-generator.ipynb new file mode 100644 index 0000000..3fad348 --- /dev/null +++ b/add-on_database-generator.ipynb @@ -0,0 +1,288 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "e3769b22", + "metadata": {}, + "source": [ + "# Introduction\n", + "This script is an add-on to the project management tool. Most translation agencies already have their own databases (or apps) and would therefore not need an extra script to generate/export them. However, for agencies which do not yet have a handy way to record their freelancer and project information in a structured way and/or export it to a usable file format, here is a script to list the freelancer and project information as dictionaries exported to json-files." + ] + }, + { + "cell_type": "markdown", + "id": "7b4bbba8", + "metadata": {}, + "source": [ + "# Freelancers\n", + "The entire freelancers database will be exported as a single file containing a list of dictionaries." + ] + }, + { + "cell_type": "markdown", + "id": "efe751b4", + "metadata": {}, + "source": [ + "## Creating a list of dictionaries\n", + "The information of each freelancer is recorded in a single dictionary. Said information comprises:\n", + "- the freelancer's full `name` (in First name Last name format), a string,\n", + "- the freelancer's `email` address, a string,\n", + "- the freelancer's `phone`number, a string,\n", + "- the freelancer's `reference`, which is their unique identifier in the agency's database (this reference is also the name of the dictionary with that freelancer's information), a string,\n", + "- the freelancer's `task(s)`, so whether they are a translator and/or a reviewer, a set,\n", + "- the freelancer's `languages`, a set." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "c90216bc", + "metadata": {}, + "outputs": [], + "source": [ + "sdw = {\n", + " 'name' : 'Sibylle de Woot',\n", + " 'email' : 'sdewoot@email.be',\n", + " 'phone' : '+32 485 12 34 56',\n", + " 'ref' : 'sdw',\n", + " 'task' : [\n", + " 'translator',\n", + " 'reviewer'\n", + " ],\n", + " 'language' : [\n", + " 'FR',\n", + " 'NL',\n", + " 'EN',\n", + " 'DE'\n", + " ]\n", + "}\n", + "mm = {\n", + " 'name' : 'Mariana Montes',\n", + " 'email' : 'mariana.montes@company.com',\n", + " 'phone' : '+32 487 98 76 54',\n", + " 'ref' : 'mm',\n", + " 'task' : [\n", + " 'reviewer'\n", + " ],\n", + " 'language' : [\n", + " 'ES',\n", + " 'EN'\n", + " ]\n", + "}\n", + "evdl = {\n", + " 'name' : 'Emily van der Londen',\n", + " 'email' : 'evdl@translation.net',\n", + " 'phone' : '+32 486 19 28 37',\n", + " 'ref' : 'evdl',\n", + " 'task' : [\n", + " 'translator'\n", + " ],\n", + " 'language' : [\n", + " 'NL',\n", + " 'EN',\n", + " 'FR'\n", + " ]\n", + "}" + ] + }, + { + "cell_type": "markdown", + "id": "af21cd42", + "metadata": {}, + "source": [ + "The dictionaries are then assembled together into a list:" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2bdb15d0", + "metadata": {}, + "outputs": [], + "source": [ + "freelancers = [sdw, mm, evdl]" + ] + }, + { + "cell_type": "markdown", + "id": "db81bdc2", + "metadata": {}, + "source": [ + "## Creating the file\n", + "This list is then dumped into (= exported as) a json-file called `freelancers_db.json`:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "eab0e236", + "metadata": {}, + "outputs": [], + "source": [ + "import json # to generate a json-file, the json-module is necessary\n", + "with open('freelancers_db.json', 'w', encoding='utf-8') as f:\n", + " json.dump(freelancers, f)" + ] + }, + { + "cell_type": "markdown", + "id": "74525c2f", + "metadata": {}, + "source": [ + "# Projects\n", + "Unlike the freelancers' information, each project's information will also be exported into a separate file (as we need one file per project for the project management tool to work, see main script)." + ] + }, + { + "cell_type": "markdown", + "id": "34707a69", + "metadata": {}, + "source": [ + "## Creating the dictionaries\n", + "The method is the same as for the freelancer database, only the information is obviously different:\n", + "- `title` (a string, ) indicates the project's title (typically the title of the source document, or the overall title the translator gave the project if there's more than one document to be translated);\n", + "- `client` (a string) indicates the client who ordered the translation;\n", + "- `source` (a string) indicates the language of the source document (document to be translated);\n", + "- `target` (a string) indicates the language of the target document (translation);\n", + "- `words` (an integer) indicates the word count of the source document;\n", + "- `start` (a string) indicates the project's start date in ISO format (YYYY-MM-DD);\n", + "- `deadline` (a string) indicates the project's deadline in ISO format (YYYY-MM-DD);\n", + "- `price` (a float) indicates the total price invoiced to the client (excl. VAT);\n", + "- `tm` (a boolean) indicates whether or not a translation memory is available for this project;\n", + "- `translator`(a string, optional, defaults to \"internal\") gives the internal reference of the freelance translator assigned to the project, or remains empty if the project was assigned to an internal translator;\n", + "- `reviewer`(a string, optional, defaults to \"internal\") gives the internal reference of the freelance reviewer assigned to the project, or remains empty if the project was assigned to an internal reviewer;\n", + "- `status`(a string, optional, defaults to \"created\") indicated the project status in the agency workflow, choosing from \"created\", \"in translation\", \"in revision\", \"delivered\", \"delayed\", or \"cancel(l)ed\";\n", + "- `domain` (a string, optional, defaults to empty string) indicates the overall domain to which the project belongs." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "2b030111", + "metadata": {}, + "outputs": [], + "source": [ + "rhumatismes_inflammatoires = {\n", + " 'title' : 'La polyarthrite rhumatoïde et autres rhumatismes inflammatoires',\n", + " 'client' : 'Reuma vzw',\n", + " 'source' : 'FR',\n", + " 'target' : 'NL',\n", + " 'words' : 2142,\n", + " 'start' : '2020-10-02',\n", + " 'deadline' : '2020-10-15',\n", + " 'price' : 715.00,\n", + " 'tm' : False,\n", + " 'translator' : '',\n", + " 'reviewer' : '',\n", + " 'status' : 'delivered',\n", + " 'domain' : 'healthcare'\n", + "}\n", + "handboek = {\n", + " 'title' : 'Handboek voor studentenvertegenwoordigers',\n", + " 'client' : 'KU Leuven',\n", + " 'source' : 'NL',\n", + " 'target' : 'EN',\n", + " 'words' : 7237,\n", + " 'start' : '2023-02-21',\n", + " 'deadline' : '2023-03-07',\n", + " 'price' : 2680.00,\n", + " 'tm' : True,\n", + " 'translator' : 'sdw',\n", + " 'reviewer' : '',\n", + " 'status' : 'delayed', \n", + " 'domain' : 'education'\n", + "}\n", + "user_guide = {\n", + " 'title' : 'User Guide MFPs',\n", + " 'client' : 'UGent',\n", + " 'source' : 'EN',\n", + " 'target' : 'NL',\n", + " 'words' : 1852,\n", + " 'start' : '2023-04-12',\n", + " 'deadline' : '2023-04-15',\n", + " 'price' : 740.00,\n", + " 'tm' : True,\n", + " 'translator' : '',\n", + " 'reviewer' : 'mm',\n", + " 'status' : 'cancelled',\n", + " 'domain' : ''\n", + "}\n", + "guide_bruxelles = {\n", + " 'title' : 'Guide de Bruxelles',\n", + " 'client' : 'Foodies',\n", + " 'source' : 'NL',\n", + " 'target' : 'FR',\n", + " 'words' : 11500,\n", + " 'start' : '2023-05-06',\n", + " 'deadline' : '2023-06-30',\n", + " 'price' : 4025.00,\n", + " 'tm' : False,\n", + " 'translator' : 'evdl',\n", + " 'reviewer' : 'sdw',\n", + " 'status' : 'in revision', \n", + " 'domain' : ''\n", + "}" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "d6460ed3", + "metadata": {}, + "outputs": [], + "source": [ + "projects = [rhumatismes_inflammatoires, handboek, user_guide, guide_bruxelles]" + ] + }, + { + "cell_type": "markdown", + "id": "92084ac1", + "metadata": {}, + "source": [ + "## Creating the files\n", + "Unlike the freelancers database, each project is also exported separately into one json-file:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "e9ff8b20", + "metadata": {}, + "outputs": [], + "source": [ + "with open('projects_db.json', 'w', encoding='utf-8') as f:\n", + " json.dump(projects, f)\n", + "\n", + "with open('rhumatismes_inflammatoires.json', 'w', encoding='utf-8') as f:\n", + " json.dump(rhumatismes_inflammatoires, f)\n", + "with open('handboek.json', 'w', encoding='utf-8') as f:\n", + " json.dump(handboek, f)\n", + "with open('user_guide.json', 'w', encoding='utf-8') as f:\n", + " json.dump(user_guide, f)\n", + "with open('guide_bruxelles.json', 'w', encoding='utf-8') as f:\n", + " json.dump(guide_bruxelles, f)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/add-on_database-generator.md b/add-on_database-generator.md new file mode 100644 index 0000000..d1bf077 --- /dev/null +++ b/add-on_database-generator.md @@ -0,0 +1,184 @@ +# Introduction +This script is an add-on to the project management tool. Most translation agencies already have their own databases (or apps) and would therefore not need an extra script to generate/export them. However, for agencies which do not yet have a handy way to record their freelancer and project information in a structured way and/or export it to a usable file format, here is a script to list the freelancer and project information as dictionaries exported to json-files. + +# Freelancers +The entire freelancers database will be exported as a single file containing a list of dictionaries. + +## Creating a list of dictionaries +The information of each freelancer is recorded in a single dictionary. Said information comprises: +- the freelancer's full `name` (in First name Last name format), a string, +- the freelancer's `email` address, a string, +- the freelancer's `phone`number, a string, +- the freelancer's `reference`, which is their unique identifier in the agency's database (this reference is also the name of the dictionary with that freelancer's information), a string, +- the freelancer's `task(s)`, so whether they are a translator and/or a reviewer, a set, +- the freelancer's `languages`, a set. + + +```python +sdw = { + 'name' : 'Sibylle de Woot', + 'email' : 'sdewoot@email.be', + 'phone' : '+32 485 12 34 56', + 'ref' : 'sdw', + 'task' : [ + 'translator', + 'reviewer' + ], + 'language' : [ + 'FR', + 'NL', + 'EN', + 'DE' + ] +} +mm = { + 'name' : 'Mariana Montes', + 'email' : 'mariana.montes@company.com', + 'phone' : '+32 487 98 76 54', + 'ref' : 'mm', + 'task' : [ + 'reviewer' + ], + 'language' : [ + 'ES', + 'EN' + ] +} +evdl = { + 'name' : 'Emily van der Londen', + 'email' : 'evdl@translation.net', + 'phone' : '+32 486 19 28 37', + 'ref' : 'evdl', + 'task' : [ + 'translator' + ], + 'language' : [ + 'NL', + 'EN', + 'FR' + ] +} +``` + +The dictionaries are then assembled together into a list: + + +```python +freelancers = [sdw, mm, evdl] +``` + +## Creating the file +This list is then dumped into (= exported as) a json-file called `freelancers_db.json`: + + +```python +import json # to generate a json-file, the json-module is necessary +with open('freelancers_db.json', 'w', encoding='utf-8') as f: + json.dump(freelancers, f) +``` + +# Projects +Unlike the freelancers' information, each project's information will also be exported into a separate file (as we need one file per project for the project management tool to work, see main script). + +## Creating the dictionaries +The method is the same as for the freelancer database, only the information is obviously different: +- `title` (a string, ) indicates the project's title (typically the title of the source document, or the overall title the translator gave the project if there's more than one document to be translated); +- `client` (a string) indicates the client who ordered the translation; +- `source` (a string) indicates the language of the source document (document to be translated); +- `target` (a string) indicates the language of the target document (translation); +- `words` (an integer) indicates the word count of the source document; +- `start` (a string) indicates the project's start date in ISO format (YYYY-MM-DD); +- `deadline` (a string) indicates the project's deadline in ISO format (YYYY-MM-DD); +- `price` (a float) indicates the total price invoiced to the client (excl. VAT); +- `tm` (a boolean) indicates whether or not a translation memory is available for this project; +- `translator`(a string, optional, defaults to "internal") gives the internal reference of the freelance translator assigned to the project, or remains empty if the project was assigned to an internal translator; +- `reviewer`(a string, optional, defaults to "internal") gives the internal reference of the freelance reviewer assigned to the project, or remains empty if the project was assigned to an internal reviewer; +- `status`(a string, optional, defaults to "created") indicated the project status in the agency workflow, choosing from "created", "in translation", "in revision", "delivered", "delayed", or "cancel(l)ed"; +- `domain` (a string, optional, defaults to empty string) indicates the overall domain to which the project belongs. + + +```python +rhumatismes_inflammatoires = { + 'title' : 'La polyarthrite rhumatoïde et autres rhumatismes inflammatoires', + 'client' : 'Reuma vzw', + 'source' : 'FR', + 'target' : 'NL', + 'words' : 2142, + 'start' : '2020-10-02', + 'deadline' : '2020-10-15', + 'price' : 715.00, + 'tm' : False, + 'translator' : '', + 'reviewer' : '', + 'status' : 'delivered', + 'domain' : 'healthcare' +} +handboek = { + 'title' : 'Handboek voor studentenvertegenwoordigers', + 'client' : 'KU Leuven', + 'source' : 'NL', + 'target' : 'EN', + 'words' : 7237, + 'start' : '2023-02-21', + 'deadline' : '2023-03-07', + 'price' : 2680.00, + 'tm' : True, + 'translator' : 'sdw', + 'reviewer' : '', + 'status' : 'delayed', + 'domain' : 'education' +} +user_guide = { + 'title' : 'User Guide MFPs', + 'client' : 'UGent', + 'source' : 'EN', + 'target' : 'NL', + 'words' : 1852, + 'start' : '2023-04-12', + 'deadline' : '2023-04-15', + 'price' : 740.00, + 'tm' : True, + 'translator' : '', + 'reviewer' : 'mm', + 'status' : 'cancelled', + 'domain' : '' +} +guide_bruxelles = { + 'title' : 'Guide de Bruxelles', + 'client' : 'Foodies', + 'source' : 'NL', + 'target' : 'FR', + 'words' : 11500, + 'start' : '2023-05-06', + 'deadline' : '2023-06-30', + 'price' : 4025.00, + 'tm' : False, + 'translator' : 'evdl', + 'reviewer' : 'sdw', + 'status' : 'in revision', + 'domain' : '' +} +``` + + +```python +projects = [rhumatismes_inflammatoires, handboek, user_guide, guide_bruxelles] +``` + +## Creating the files +Unlike the freelancers database, each project is also exported separately into one json-file: + + +```python +with open('projects_db.json', 'w', encoding='utf-8') as f: + json.dump(projects, f) + +with open('rhumatismes_inflammatoires.json', 'w', encoding='utf-8') as f: + json.dump(rhumatismes_inflammatoires, f) +with open('handboek.json', 'w', encoding='utf-8') as f: + json.dump(handboek, f) +with open('user_guide.json', 'w', encoding='utf-8') as f: + json.dump(user_guide, f) +with open('guide_bruxelles.json', 'w', encoding='utf-8') as f: + json.dump(guide_bruxelles, f) +``` diff --git a/freelancers_db.json b/freelancers_db.json new file mode 100644 index 0000000..05bebbe --- /dev/null +++ b/freelancers_db.json @@ -0,0 +1 @@ +[{"name": "Sibylle de Woot", "email": "sdewoot@email.be", "phone": "+32 485 12 34 56", "ref": "sdw", "task": ["translator", "reviewer"], "language": ["FR", "NL", "EN", "DE"]}, {"name": "Mariana Montes", "email": "mariana.montes@company.com", "phone": "+32 487 98 76 54", "ref": "mm", "task": ["reviewer"], "language": ["ES", "EN"]}, {"name": "Emily van der Londen", "email": "evdl@translation.net", "phone": "+32 486 19 28 37", "ref": "evdl", "task": ["translator"], "language": ["NL", "EN", "FR"]}] \ No newline at end of file diff --git a/guide_bruxelles.json b/guide_bruxelles.json new file mode 100644 index 0000000..78ed310 --- /dev/null +++ b/guide_bruxelles.json @@ -0,0 +1 @@ +{"title": "Guide de Bruxelles", "client": "Foodies", "source": "NL", "target": "FR", "words": 11500, "start": "2023-05-06", "deadline": "2023-06-30", "price": 4025.0, "tm": false, "translator": "evdl", "reviewer": "sdw", "status": "in revision", "domain": ""} \ No newline at end of file diff --git a/handboek.json b/handboek.json new file mode 100644 index 0000000..7b36710 --- /dev/null +++ b/handboek.json @@ -0,0 +1 @@ +{"title": "Handboek voor studentenvertegenwoordigers", "client": "KU Leuven", "source": "NL", "target": "EN", "words": 7237, "start": "2023-02-21", "deadline": "2023-03-07", "price": 2680.0, "tm": true, "translator": "sdw", "reviewer": "", "status": "delayed", "domain": "education"} \ No newline at end of file diff --git a/project-management-tool_basic.py b/project-management-tool_basic.py new file mode 100644 index 0000000..3c96ad4 --- /dev/null +++ b/project-management-tool_basic.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python +# coding: utf-8 + +# In[ ]: + + +import datetime + +"""This script gives a translation agency an overview of all its projects. +""" + +import argparse + +class Project: + + def __init__( + self, + title, + client, + source, + target, + words, + start, + deadline, + price, + tm, + translator = 'internal', + reviewer = 'internal', + status = 'created', + domain = ''): + """Initialises an object of the Project class, a class that represents a translation project of a translation agency. + + Args: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + translator (string, optional): Translator assigned to the project. Defaults to 'internal'. + reviewer (string, optional): Reviewer assigned to the project. Defaults to 'internal'. + status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'. + domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string. + + Attributes: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + translator (string, optional): Translator assigned to the project. + reviewer (string, optional): Reviewer assigned to the project. + status (string, optional): Current project status inside the agency's workflow. + domain (str): Overall domain to which the project belongs. + today (date): Date of the day where the script is run. + st (date): Project's start date, turned from an ISO-formatted string into a date. + dl (date): Project's deadline, turned from an ISO-formatted string into a date. + daysleft (timedelta): Time left until the project's deadline. + length (timedelta): Total time allocated to the project. + rate (float): Project's word rate. + efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek). + + Raises: + TypeError: If 'title' is not a string. + TypeError: If 'client' is not a string. + TypeError: If 'source' is not a string. + TypeError: If 'target' is not a string. + TypeError: If 'words' is not an integer. + TypeError: If 'start' is not a string. + ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'deadline' is not a string. + ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'price' is not a float. + TypeError: If 'tm' is not a boolean. + TypeError: If 'translator' is not a string. + TypeError: If 'reviewer' is not a string. + TypeError: If 'status' is not a string. + ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed. + TypeError: If 'domain' is not a string. + """ + if type(title) != str: + raise TypeError("The title must be a string.") + else: + self.title = title + if type(client) != str: + raise TypeError("The client name must be a string.") + else: + self.client = client + if type(source) != str: + raise TypeError("The source language must be a string.") + else: + self.source = source + if type(target) != str: + raise TypeError("The target langague must be a string.") + else: + self.target = target + if type(words) != int: + raise TypeError("The word count must be an integer.") + else: + self.words = words + if type(start) != str: + raise TypeError("The start date must be provided as a string.") + try: + self.st = datetime.date.fromisoformat(start) + except: + raise TypeError("The start date must be provided in ISOFormat") + else: + self.start = start + if type(deadline) != str: + raise TypeError("The deadline must be provided as a string.") + try: + self.dl = datetime.date.fromisoformat(deadline) + except: + raise TypeError("The start deadline must be provided in ISOFormat") + else: + self.deadline = deadline + if type(price) != float: + raise TypeError("The price must be a float.") + else: + self.price = price + if type(tm) != bool: + raise TypeError("The TM availability must be a boolean.") + else: + self.tm = tm + if type(translator) != str: + raise TypeError("The translator's name must be a string.") + elif translator == '': + self.translator = "internal" + else: + self.translator = translator + if type(reviewer) != str: + raise TypeError("The reviewer's name must be a string.") + elif reviewer == '': + self.reviewer = "internal" + else: + self.reviewer = reviewer + if type(status) != str: + raise TypeError("The status must be a string.") + elif status == '': + self.status = "created" + elif not status.lower() in ["created", + "in translation", + "in revision", + "delivered", + "delayed", + "cancelled", + "canceled"]: + raise ValueError("Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.") + else: + self.status = status + if type(domain) != str: + raise TypeError("The domain must be a string.") + else: + self.domain = domain + + today = datetime.date.today() + self.daysleft = self.dl - today + self.length = self.dl - self.st + self.rate = self.price/self.words + self.efficiency = words/((5/7)*self.length.days) + + def days_left(self): + """Displays how many days remain until the project deadline. + + Args: + None + + Returns: + str: A message informing the user that the deadline has been exceeded already if the deadline is in the past. + str: A message informing the user of the number of days left until the deadline if the deadline is not in the past. + """ + if self.dl < datetime.date.today(): + return f"The deadline has been exceeded already." + else: + return f"There are {self.daysleft.days} days left until the deadline." + + def project_length(self): + """Displays the total number of days allocated to the project. + + Args: + None + + Returns: + str: The total number of days allocated to the project. + """ + return f"{self.length.days} days" + + def __str__(self): + # defines the print behaviour: returns a text providing the main information about the project + sent_1 = f"{self.title} is a translation for {self.client} from {self.source} into {self.target}." + if self.translator.lower() == "internal" and self.reviewer.lower() == "internal": + sent_2 = f"Both the translator and the reviewer are agency collaborators." + elif self.translator.lower() == "internal" and self.reviewer.lower() != "internal": + sent_2 = f"The translator is an agency collaborator and the reviewer is {self.reviewer}." + elif self.translator.lower() != "internal" and self.reviewer.lower() == "internal": + sent_2 = f"The translator is {self.translator} and the reviewer is an agency collaborator." + else: + sent_2 = f"The translator is {self.translator} and the reviewer is {self.reviewer}." + # this if-statement considers whether a domain was added + if len(self.domain) > 0: + sent_3 = f"The domain is: {self.domain}." + else: + sent_3 = "The domain is unspecified." # if no domain was added, the text mentions it + sent_4 = f"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word." #the word rate is rounded to two decimal places to avoid cumbersomely long numbers + # this if-statement considers whether the deadline is in the past + if self.dl < datetime.date.today(): + sent_5 = f"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day." # the efficiency is rounded to units because you can't translate a fraction of a word anyway + else: + sent_5 = f"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day." + # this if-statement considers whether there is a translation memory for the project + sent_6 = f"There is {'a' if self.tm else 'no'} translation memory." + sent_7 = f"The project is currently {self.status}." + # print each sentence in a different line + return "\n".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7]) + +if __name__ == "__main__": + parser = argparse.ArgumentParser("project") + parser.add_argument("title", type=str, help="Title of the project") + parser.add_argument("client", type=str, help="Client who ordered the translation") + parser.add_argument("source", type=str, help="Source language") + parser.add_argument("target", type=str, help="Target language") + parser.add_argument("words", type=int, help="Project word count") + parser.add_argument("start", type=str, help="Project start date") + parser.add_argument("deadline", type=str, help="Project deadline") + parser.add_argument("price", type=float, help="Total price project") + parser.add_argument("tm", type=bool, help="Whether a translation memory is available") + parser.add_argument("--translator", type=str, default = "internal", help="Translator assigned to the project") + parser.add_argument("--revisor", type=str, default = "internal",help="Revisor assigned to the project") + parser.add_argument("--status", type=str, default = "created", + choices = ["created", "in translation", "in revision", "delivered", "delayed", "cancelled", "canceled"], help="Project status in the agency workflow") + parser.add_argument("--domain", type=str, default = "", help="Overall domain of the text") + args = parser.parse_args() + proj = Project(args.title, args.client, args.source, args.target, args.words, args.start, args.deadline, args.price, args.tm, args.translator, args.revisor, args.status, args.domain) + proj.days_left() + proj.project_length() + print(proj) + diff --git a/project-management-tool_expanded.ipynb b/project-management-tool_expanded.ipynb new file mode 100644 index 0000000..5dc3b83 --- /dev/null +++ b/project-management-tool_expanded.ipynb @@ -0,0 +1,1683 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "14eadce4", + "metadata": {}, + "source": [ + "# The two classes\n", + "Below, you'll find the two classes described in `README.md`. Make their respective cells run to initialise the classes." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "2480b610", + "metadata": {}, + "outputs": [], + "source": [ + "import datetime\n", + "\n", + "\"\"\"This script gives a translation agency an overview of all its projects.\n", + "\"\"\"\n", + "\n", + "class Project:\n", + "\n", + " def __init__(\n", + " self,\n", + " title,\n", + " client,\n", + " source,\n", + " target,\n", + " words,\n", + " start,\n", + " deadline,\n", + " price,\n", + " tm,\n", + " translator = 'internal',\n", + " reviewer = 'internal',\n", + " status = 'created',\n", + " domain = ''):\n", + " \"\"\"Initialises an object of the Project class, a class that represents a translation project of a translation agency.\n", + " \n", + " Args:\n", + " title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated).\n", + " client (str): Client who ordered the translation.\n", + " source (str): Language of the source document(s) (document(s) to be translated).\n", + " target (str): Language of the target document(s) (translation(s)).\n", + " words (int): Word count of the source document(s).\n", + " start (str): Project's start date in ISO format (YYYY-MM-DD).\n", + " deadline (str): Project's deadline in ISO format (YYYY-MM-DD).\n", + " price (float): Total price invoiced to the client (excl. VAT).\n", + " tm (bool): Whether a translation memory is available for this project.\n", + " translator (string, optional): Translator assigned to the project. Defaults to 'internal'.\n", + " reviewer (string, optional): Reviewer assigned to the project. Defaults to 'internal'.\n", + " status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'.\n", + " domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string.\n", + " \n", + " Attributes:\n", + " title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated).\n", + " client (str): Client who ordered the translation.\n", + " source (str): Language of the source document(s) (document(s) to be translated).\n", + " target (str): Language of the target document(s) (translation(s)).\n", + " words (int): Word count of the source document(s).\n", + " start (str): Project's start date in ISO format (YYYY-MM-DD).\n", + " deadline (str): Project's deadline in ISO format (YYYY-MM-DD).\n", + " price (float): Total price invoiced to the client (excl. VAT).\n", + " tm (bool): Whether a translation memory is available for this project.\n", + " translator (string, optional): Translator assigned to the project.\n", + " reviewer (string, optional): Reviewer assigned to the project.\n", + " status (string, optional): Current project status inside the agency's workflow.\n", + " domain (str): Overall domain to which the project belongs.\n", + " today (date): Date of the day where the script is run.\n", + " st (date): Project's start date, turned from an ISO-formatted string into a date.\n", + " dl (date): Project's deadline, turned from an ISO-formatted string into a date.\n", + " daysleft (timedelta): Time left until the project's deadline.\n", + " length (timedelta): Total time allocated to the project.\n", + " rate (float): Project's word rate.\n", + " efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek).\n", + " \n", + " Raises:\n", + " TypeError: If 'title' is not a string.\n", + " TypeError: If 'client' is not a string.\n", + " TypeError: If 'source' is not a string.\n", + " TypeError: If 'target' is not a string.\n", + " TypeError: If 'words' is not an integer.\n", + " TypeError: If 'start' is not a string.\n", + " ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits.\n", + " TypeError: If 'deadline' is not a string.\n", + " ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits.\n", + " TypeError: If 'price' is not a float.\n", + " TypeError: If 'tm' is not a boolean.\n", + " TypeError: If 'translator' is not a string.\n", + " TypeError: If 'reviewer' is not a string.\n", + " TypeError: If 'status' is not a string.\n", + " ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed.\n", + " TypeError: If 'domain' is not a string.\n", + " \"\"\"\n", + " if type(title) != str:\n", + " raise TypeError(\"The title must be a string.\")\n", + " else:\n", + " self.title = title\n", + " if type(client) != str:\n", + " raise TypeError(\"The client name must be a string.\")\n", + " else:\n", + " self.client = client\n", + " if type(source) != str:\n", + " raise TypeError(\"The source language must be a string.\")\n", + " else:\n", + " self.source = source\n", + " if type(target) != str:\n", + " raise TypeError(\"The target langague must be a string.\")\n", + " else:\n", + " self.target = target\n", + " if type(words) != int:\n", + " raise TypeError(\"The word count must be an integer.\")\n", + " else:\n", + " self.words = words\n", + " if type(start) != str:\n", + " raise TypeError(\"The start date must be provided as a string.\")\n", + " try:\n", + " self.st = datetime.date.fromisoformat(start)\n", + " except:\n", + " raise TypeError(\"The start date must be provided in ISO format\")\n", + " else:\n", + " self.start = start\n", + " if type(deadline) != str:\n", + " raise TypeError(\"The deadline must be provided as a string.\")\n", + " try:\n", + " self.dl = datetime.date.fromisoformat(deadline)\n", + " except:\n", + " raise TypeError(\"The deadline must be provided in ISO format\")\n", + " else:\n", + " self.deadline = deadline\n", + " if type(price) != float:\n", + " raise TypeError(\"The price must be a float.\")\n", + " else:\n", + " self.price = price\n", + " if type(tm) != bool:\n", + " raise TypeError(\"The TM availability must be a boolean.\")\n", + " else:\n", + " self.tm = tm\n", + " if type(translator) != str and type(translator) != Freelancer:\n", + " raise TypeError(\"The translator's name must be a string or an entry in our freelancer database.\")\n", + " elif translator == '':\n", + " self.translator = \"internal\"\n", + " else:\n", + " self.translator = translator\n", + " if type(reviewer) != str and type(reviewer) != Freelancer:\n", + " raise TypeError(\"The reviewer's name must be a string or an entry in our freelancer database.\")\n", + " elif reviewer == '':\n", + " self.reviewer = \"internal\"\n", + " else:\n", + " self.reviewer = reviewer\n", + " if type(status) != str:\n", + " raise TypeError(\"The status must be a string.\")\n", + " elif status == '':\n", + " self.status = \"created\"\n", + " elif not status.lower() in [\"created\",\n", + " \"in translation\",\n", + " \"in revision\",\n", + " \"delivered\",\n", + " \"delayed\",\n", + " \"cancelled\",\n", + " \"canceled\"]:\n", + " raise ValueError(\"Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.\")\n", + " else:\n", + " self.status = status\n", + " if type(domain) != str:\n", + " raise TypeError(\"The domain must be a string.\")\n", + " else:\n", + " self.domain = domain\n", + " \n", + " today = datetime.date.today()\n", + " self.daysleft = self.dl - today\n", + " self.length = self.dl - self.st\n", + " self.rate = self.price/self.words\n", + " self.efficiency = words/((5/7)*self.length.days)\n", + "\n", + " def days_left(self):\n", + " \"\"\"Displays how many days remain until the project deadline.\n", + " \n", + " Args:\n", + " None\n", + " \n", + " Returns:\n", + " str: A message informing the user that the deadline has been exceeded already if the deadline is in the past.\n", + " str: A message informing the user of the number of days left until the deadline if the deadline is not in the past.\n", + " \"\"\"\n", + " if self.dl < datetime.date.today():\n", + " return f\"The deadline has been exceeded already.\"\n", + " else:\n", + " return f\"There are {self.daysleft.days} days left until the deadline.\"\n", + " \n", + " def project_length(self):\n", + " \"\"\"Displays the total number of days allocated to the project.\n", + " \n", + " Args:\n", + " None\n", + " \n", + " Returns:\n", + " str: The total number of days allocated to the project.\n", + " \"\"\"\n", + " return f\"{self.length.days} days\"\n", + " \n", + " def __str__(self):\n", + " # defines the print behaviour: returns a text providing the main information about the project\n", + " sent_1 = f\"{self.title} is a translation for {self.client} from {self.source} into {self.target}.\"\n", + " if self.translator.lower() == \"internal\" and self.reviewer.lower() == \"internal\":\n", + " sent_2 = f\"Both the translator and the reviewer are agency collaborators.\"\n", + " elif self.translator.lower() == \"internal\" and self.reviewer.lower() != \"internal\":\n", + " sent_2 = f\"The translator is an agency collaborator and the reviewer is {self.reviewer}.\"\n", + " elif self.translator.lower() != \"internal\" and self.reviewer.lower() == \"internal\":\n", + " sent_2 = f\"The translator is {self.translator} and the reviewer is an agency collaborator.\"\n", + " else:\n", + " sent_2 = f\"The translator is {self.translator} and the reviewer is {self.reviewer}.\"\n", + " # this if-statement considers whether a domain was added\n", + " if len(self.domain) > 0:\n", + " sent_3 = f\"The domain is: {self.domain}.\"\n", + " else:\n", + " sent_3 = \"The domain is unspecified.\" # if no domain was added, the text mentions it\n", + " sent_4 = f\"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word.\" #the word rate is rounded to two decimal places to avoid cumbersomely long numbers\n", + " # this if-statement considers whether the deadline is in the past\n", + " if self.dl < datetime.date.today():\n", + " sent_5 = f\"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day.\" # the efficiency is rounded to units because you can't translate a fraction of a word anyway\n", + " else:\n", + " sent_5 = f\"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day.\"\n", + " # this if-statement considers whether there is a translation memory for the project\n", + " sent_6 = f\"There is {'a' if self.tm else 'no'} translation memory.\"\n", + " sent_7 = f\"The project is currently {self.status}.\"\n", + " # print each sentence in a different line\n", + " return \"\\n\".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7])" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "14fba222", + "metadata": {}, + "outputs": [], + "source": [ + "class Freelancer():\n", + "\n", + " def __init__(self, name, email, phone, ref, task, language):\n", + " self.name = name\n", + " self.email = email\n", + " self.phone = phone\n", + " self.ref = ref\n", + " self.task = task\n", + " self.language = language\n", + " \n", + " def function_with_kwargs(**kwargs):\n", + " if \"name\" in kwargs:\n", + " print(kwargs[\"name\"])\n", + " else:\n", + " print(\"no name found\")\n", + " \n", + " def __str__(self):\n", + " sent_1 = f\"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone}).\"\n", + " sent_2 = f\"{self.name}'s reference in our database is {self.ref}.\"\n", + " sent_3 = f\"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}.\"\n", + " return \"\\n\".join([sent_1, sent_2, sent_3])" + ] + }, + { + "cell_type": "markdown", + "id": "ea7762ef", + "metadata": {}, + "source": [ + "# Creating and using objects of the Project class" + ] + }, + { + "cell_type": "markdown", + "id": "336998ee", + "metadata": {}, + "source": [ + "## Manually\n", + "It is possible to manually define objects of each class, meaning that you write out each mandatory attribute (and any optional attribute you want to make deviate from the default value), for example:" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "306f2ab3", + "metadata": {}, + "outputs": [], + "source": [ + "project1 = Project(\"The project title\", \"some client\", \"FR\", \"EN\", 10000, \"2023-06-17\", \"2023-06-30\", 3600.00, True)" + ] + }, + { + "cell_type": "markdown", + "id": "e9b9334d", + "metadata": {}, + "source": [ + "In this case, the name of the object is \"project1\" and no optional attribute differs from the default value. But you can also change all the optional attributes:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "bcfe2da8", + "metadata": {}, + "outputs": [], + "source": [ + "project2 = Project(\"A second project title\", \"another client\", \"NL\", \"DE\", 7500, \"2023-06-16\", \"2023-06-28\", 3200.00, True, \"Sibylle de Woot\", \"Mariana Montes\", \"in translation\", \"education\")" + ] + }, + { + "cell_type": "markdown", + "id": "109a2949", + "metadata": {}, + "source": [ + "Or only some of them:" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "dbddfd92", + "metadata": {}, + "outputs": [], + "source": [ + "project3 = Project(\"Some other project title\", \"new client\", \"EN\", \"NL\", 12000, \"2023-06-15\", \"2023-06-30\", 3800.00, True, reviewer = \"Sibylle de Woot\", domain = \"healthcare\")" + ] + }, + { + "cell_type": "markdown", + "id": "3f3586f5", + "metadata": {}, + "source": [ + "Once an object has been created, you can use the various methods to\n", + "- Print an overview of all the project information:" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "7390c272", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The project title is a translation for some client from FR into EN.\n", + "Both the translator and the reviewer are agency collaborators.\n", + "The domain is unspecified.\n", + "It's 10000 words long, with a rate of 0.36 € per word.\n", + "It started on 2023-06-17 and is due on 2023-06-30, so 13 days are foreseen for it, of which 12 left. To meet the deadline, 1077 words need to be translated or revised per day.\n", + "There is a translation memory.\n", + "The project is currently created.\n" + ] + } + ], + "source": [ + "print(project1)\n", + "# The printing method is the only one that doesn't require you to specify that it's specific to the Project class, you simply need the name of the object" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "4c36e4ee", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "A second project title is a translation for another client from NL into DE.\n", + "The translator is Sibylle de Woot and the reviewer is Mariana Montes.\n", + "The domain is: education.\n", + "It's 7500 words long, with a rate of 0.43 € per word.\n", + "It started on 2023-06-16 and is due on 2023-06-28, so 12 days are foreseen for it, of which 10 left. To meet the deadline, 875 words need to be translated or revised per day.\n", + "There is a translation memory.\n", + "The project is currently in translation.\n" + ] + } + ], + "source": [ + "print(project2)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "80347a4a", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Some other project title is a translation for new client from EN into NL.\n", + "The translator is an agency collaborator and the reviewer is Sibylle de Woot.\n", + "The domain is: healthcare.\n", + "It's 12000 words long, with a rate of 0.32 € per word.\n", + "It started on 2023-06-15 and is due on 2023-06-30, so 15 days are foreseen for it, of which 12 left. To meet the deadline, 1120 words need to be translated or revised per day.\n", + "There is a translation memory.\n", + "The project is currently created.\n" + ] + } + ], + "source": [ + "print(project3)" + ] + }, + { + "cell_type": "markdown", + "id": "eda324f2", + "metadata": {}, + "source": [ + "- Find out how much time is left until the deadline:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "de5fc1f5", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'There are 12 days left until the deadline.'" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Project.days_left(project1)\n", + "# To use any other class method, you need to use the format \"Class.method(object)\"" + ] + }, + { + "cell_type": "markdown", + "id": "28055bbe", + "metadata": {}, + "source": [ + "- Find out how many days are foreseen in total for the project:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "9b0ac32b", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'13 days'" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Project.project_length(project1)" + ] + }, + { + "cell_type": "markdown", + "id": "0ecd4c69", + "metadata": {}, + "source": [ + "Attributes can also be retrieved separately, with `object.attribute`, for example" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "ceaf14b8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'The project title'" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "project1.title" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "a4c670b8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'2023-06-17'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "project1.start" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "6531970a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.timedelta(days=13)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "project1.length" + ] + }, + { + "cell_type": "markdown", + "id": "a87fe061", + "metadata": {}, + "source": [ + "As you can see, the value of the attribute \"length\" isn't very user-friendly, as it is a timedelta computed with the datetime module. That is the reason why the method `project_length` was created, to display the attribute in a more readable way. Another user-friendely ways to display the project length is by printing the attribute, rather than simply retrieving it:" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "12f49f53", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "13 days, 0:00:00\n" + ] + } + ], + "source": [ + "print(project1.length)" + ] + }, + { + "cell_type": "markdown", + "id": "3c848675", + "metadata": {}, + "source": [ + "As you can see, the project length is calculated to the second here. For now, that's not useful, but when a future update allows PMs to specify the start and end time, it will be." + ] + }, + { + "cell_type": "markdown", + "id": "3d34827c", + "metadata": {}, + "source": [ + "So, feeding the project information into an object of the `Project` class makes for easy information retrieval and allows the PM to generate a clear global overview of the project in full sentences. However, especially with manual information input, issues can occur when the user makes a typo, or inputs the wrong type of variable. To counter those issues, validation was integrated into the `Project` class. Any wrong variable type raises an error, and the user is asked to use the right variable type.\n", + "\n", + "Say, for example, that I forget that the price needs to be a float (i.e. a decimal number):" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f56b92bc", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "The price must be a float.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[15], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m project4 \u001b[38;5;241m=\u001b[39m \u001b[43mProject\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mYet another title\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43manother client\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mFR\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mNL\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m8000\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m2023-06-16\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m2023-06-28\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3300\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mSibylle de Woot\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mMariana Montes\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43min translation\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtechnical\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[1], line 117\u001b[0m, in \u001b[0;36mProject.__init__\u001b[0;34m(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain)\u001b[0m\n\u001b[1;32m 115\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdeadline \u001b[38;5;241m=\u001b[39m deadline\n\u001b[1;32m 116\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mtype\u001b[39m(price) \u001b[38;5;241m!=\u001b[39m \u001b[38;5;28mfloat\u001b[39m:\n\u001b[0;32m--> 117\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe price must be a float.\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 118\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 119\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mprice \u001b[38;5;241m=\u001b[39m price\n", + "\u001b[0;31mTypeError\u001b[0m: The price must be a float." + ] + } + ], + "source": [ + "project4 = Project(\"Yet another title\", \"another client\", \"FR\", \"NL\", 8000, \"2023-06-16\", \"2023-06-28\", 3300, True, \"Sibylle de Woot\", \"Mariana Montes\", \"in translation\", \"technical\")" + ] + }, + { + "cell_type": "markdown", + "id": "7de66ade", + "metadata": {}, + "source": [ + "As you can see, the script informs me that the price must be a float. Now, let's say that my numbers are correct, but that I didn't write my dates in ISO format:" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "6e08020c", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "The start date must be provided in ISO format", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[1], line 103\u001b[0m, in \u001b[0;36mProject.__init__\u001b[0;34m(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain)\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 103\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mst \u001b[38;5;241m=\u001b[39m \u001b[43mdatetime\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdate\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfromisoformat\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstart\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 104\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m:\n", + "\u001b[0;31mValueError\u001b[0m: Invalid isoformat string: '23-06-16'", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[16], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m project4 \u001b[38;5;241m=\u001b[39m \u001b[43mProject\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mYet another title\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43manother client\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mFR\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mNL\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m8000\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m23-06-16\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m23-06-28\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3300.00\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mSibylle de Woot\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mMariana Montes\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43min translation\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtechnical\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[1], line 105\u001b[0m, in \u001b[0;36mProject.__init__\u001b[0;34m(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain)\u001b[0m\n\u001b[1;32m 103\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mst \u001b[38;5;241m=\u001b[39m datetime\u001b[38;5;241m.\u001b[39mdate\u001b[38;5;241m.\u001b[39mfromisoformat(start)\n\u001b[1;32m 104\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m:\n\u001b[0;32m--> 105\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe start date must be provided in ISO format\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 106\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 107\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstart \u001b[38;5;241m=\u001b[39m start\n", + "\u001b[0;31mTypeError\u001b[0m: The start date must be provided in ISO format" + ] + } + ], + "source": [ + "project4 = Project(\"Yet another title\", \"another client\", \"FR\", \"NL\", 8000, \"23-06-16\", \"23-06-28\", 3300.00, True, \"Sibylle de Woot\", \"Mariana Montes\", \"in translation\", \"technical\")" + ] + }, + { + "cell_type": "markdown", + "id": "c9c92274", + "metadata": {}, + "source": [ + "While both dates are wrong, only the error with the start date is raised. That's because the script stops at the first detected error. However, if I only correct one and not the other, it will raise the error in the deadline:" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "6d4f884f", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "The deadline must be provided in ISO format", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[1], line 111\u001b[0m, in \u001b[0;36mProject.__init__\u001b[0;34m(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain)\u001b[0m\n\u001b[1;32m 110\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 111\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdl \u001b[38;5;241m=\u001b[39m \u001b[43mdatetime\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdate\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfromisoformat\u001b[49m\u001b[43m(\u001b[49m\u001b[43mdeadline\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 112\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m:\n", + "\u001b[0;31mValueError\u001b[0m: Invalid isoformat string: '23-06-28'", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[17], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m project4 \u001b[38;5;241m=\u001b[39m \u001b[43mProject\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mYet another title\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43manother client\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mFR\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mNL\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m8000\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m2023-06-16\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m23-06-28\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3300.00\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mSibylle de Woot\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mMariana Montes\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43min translation\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtechnical\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[1], line 113\u001b[0m, in \u001b[0;36mProject.__init__\u001b[0;34m(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain)\u001b[0m\n\u001b[1;32m 111\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdl \u001b[38;5;241m=\u001b[39m datetime\u001b[38;5;241m.\u001b[39mdate\u001b[38;5;241m.\u001b[39mfromisoformat(deadline)\n\u001b[1;32m 112\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m:\n\u001b[0;32m--> 113\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe deadline must be provided in ISO format\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 114\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 115\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mdeadline \u001b[38;5;241m=\u001b[39m deadline\n", + "\u001b[0;31mTypeError\u001b[0m: The deadline must be provided in ISO format" + ] + } + ], + "source": [ + "project4 = Project(\"Yet another title\", \"another client\", \"FR\", \"NL\", 8000, \"2023-06-16\", \"23-06-28\", 3300.00, True, \"Sibylle de Woot\", \"Mariana Montes\", \"in translation\", \"technical\")" + ] + }, + { + "cell_type": "markdown", + "id": "71845103", + "metadata": {}, + "source": [ + "The way the script detects whether or not the date format is correct, is by trying to turn the string I inputted into an actual date with the datetime module. Because if that, it can also catch some errors if I provide the date in YYYY-DD-MM format, rather than YYYY-MM-DD:" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "95d045ba", + "metadata": {}, + "outputs": [ + { + "ename": "TypeError", + "evalue": "The start date must be provided in ISO format", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[1], line 103\u001b[0m, in \u001b[0;36mProject.__init__\u001b[0;34m(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain)\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m--> 103\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mst \u001b[38;5;241m=\u001b[39m \u001b[43mdatetime\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mdate\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mfromisoformat\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstart\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 104\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m:\n", + "\u001b[0;31mValueError\u001b[0m: month must be in 1..12", + "\nDuring handling of the above exception, another exception occurred:\n", + "\u001b[0;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[18], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m project4 \u001b[38;5;241m=\u001b[39m \u001b[43mProject\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mYet another title\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43manother client\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mFR\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mNL\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m8000\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m2023-16-06\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m2023-06-28\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3300.00\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mSibylle de Woot\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mMariana Montes\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43min translation\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtechnical\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[1], line 105\u001b[0m, in \u001b[0;36mProject.__init__\u001b[0;34m(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain)\u001b[0m\n\u001b[1;32m 103\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mst \u001b[38;5;241m=\u001b[39m datetime\u001b[38;5;241m.\u001b[39mdate\u001b[38;5;241m.\u001b[39mfromisoformat(start)\n\u001b[1;32m 104\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m:\n\u001b[0;32m--> 105\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe start date must be provided in ISO format\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 106\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 107\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstart \u001b[38;5;241m=\u001b[39m start\n", + "\u001b[0;31mTypeError\u001b[0m: The start date must be provided in ISO format" + ] + } + ], + "source": [ + "project4 = Project(\"Yet another title\", \"another client\", \"FR\", \"NL\", 8000, \"2023-16-06\", \"2023-06-28\", 3300.00, True, \"Sibylle de Woot\", \"Mariana Montes\", \"in translation\", \"technical\")" + ] + }, + { + "cell_type": "markdown", + "id": "a2ea9317", + "metadata": {}, + "source": [ + "When trying to convert the string into a date, the script runs into a problem, as there is no sixteenth month in the year, which leads it to ask for an ISO-formatted date. This is handy, but has its limitations for dates until the twelvth of the month, in which day and month can me inverted and still yield a valid ISO-formatted date. Validation is a useful feature to weed out many mistakes, but it's no miracle solution and in the end it's up to the user to ensure that they use correct information." + ] + }, + { + "cell_type": "markdown", + "id": "0d5f6b5a", + "metadata": {}, + "source": [ + "A last \"special\" type of validation used in the `Project` class concerns the attribute `status`. To make sure everyone is on the same page concerning project progress, only labels from the agency workflow are allowed for this attribute, namely:\n", + "- created,\n", + "- in translation,\n", + "- in revision,\n", + "- delivered,\n", + "- delayed,\n", + "- cancel(l)ed.\n", + "\n", + "If a user tries to invent their own labels, they will receive a request to use a status in the agency workflow:" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "48ee762b", + "metadata": {}, + "outputs": [ + { + "ename": "ValueError", + "evalue": "Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[19], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m project4 \u001b[38;5;241m=\u001b[39m \u001b[43mProject\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mYet another title\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43manother client\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mFR\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mNL\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m8000\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m2023-06-16\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m2023-06-28\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3300.00\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mSibylle de Woot\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mMariana Montes\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mongoing\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mtechnical\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n", + "Cell \u001b[0;32mIn[1], line 147\u001b[0m, in \u001b[0;36mProject.__init__\u001b[0;34m(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain)\u001b[0m\n\u001b[1;32m 139\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstatus \u001b[38;5;241m=\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcreated\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m 140\u001b[0m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m status\u001b[38;5;241m.\u001b[39mlower() \u001b[38;5;129;01min\u001b[39;00m [\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcreated\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 141\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124min translation\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 142\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124min revision\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 145\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcancelled\u001b[39m\u001b[38;5;124m\"\u001b[39m,\n\u001b[1;32m 146\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mcanceled\u001b[39m\u001b[38;5;124m\"\u001b[39m]:\n\u001b[0;32m--> 147\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mPlease pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[1;32m 148\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 149\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstatus \u001b[38;5;241m=\u001b[39m status\n", + "\u001b[0;31mValueError\u001b[0m: Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled." + ] + } + ], + "source": [ + "project4 = Project(\"Yet another title\", \"another client\", \"FR\", \"NL\", 8000, \"2023-06-16\", \"2023-06-28\", 3300.00, True, \"Sibylle de Woot\", \"Mariana Montes\", \"ongoing\", \"technical\")" + ] + }, + { + "cell_type": "markdown", + "id": "5d6d8b8d", + "metadata": {}, + "source": [ + "A last point you may have noticed is that, while the default value when no domain is provided is an empty string, the default value for `translator` and `reviewer` is \"internal\" and even an empty string is turned into \"internal\", even though it does not appear as such in the printed text with project information (and an empty string can be turned into \"an agency collaborator\" just as easily as a string containing \"internal\"). The reason is that, if the user wants to retrieve the value of the attribute `translator`, \"internal\" is much clearer than an empty string, which looks like missing information and which requires more reflexion on the part of the user to understand that it's empty because the project was assigned to an agency collaborator." + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "0351fbd6", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'internal'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "project1.translator" + ] + }, + { + "cell_type": "markdown", + "id": "f468f379", + "metadata": {}, + "source": [ + "## Using files" + ] + }, + { + "cell_type": "markdown", + "id": "17d73d19", + "metadata": {}, + "source": [ + "Since creating each instance of the `Project` class manually would be both cumbersome and *very* time-consuming, a way to speed up the process is to export the project information to a file and use that file to feed the information into the `Project` class. In this example, all the project information was exported as a list of dictionaries (one dictionary per project) in the json-file called `projects_db.json`. Bear in mind that, for the system to find the file, it needs to be in the folder you're running the script from (otherwise you need to use the full path to the file, but we'll not go into that here).\n", + "\n", + "If all you want is an overview of all the projects printed as text, you can go through the dictionaries one by one and print their information, without actually creating `Project` objects:" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "df01de23", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "La polyarthrite rhumatoïde et autres rhumatismes inflammatoires is a translation for Reuma vzw from FR into NL.\n", + "Both the translator and the reviewer are agency collaborators.\n", + "The domain is: healthcare.\n", + "It's 2142 words long, with a rate of 0.33 € per word.\n", + "It started on 2020-10-02 and was due on 2020-10-15, so 13 days were foreseen for it. To meet the deadline, 231 words needed to be translated or revised per day.\n", + "There is no translation memory.\n", + "The project is currently delivered.\n", + "----\n", + "Handboek voor studentenvertegenwoordigers is a translation for KU Leuven from NL into EN.\n", + "The translator is sdw and the reviewer is an agency collaborator.\n", + "The domain is: education.\n", + "It's 7237 words long, with a rate of 0.37 € per word.\n", + "It started on 2023-02-21 and was due on 2023-03-07, so 14 days were foreseen for it. To meet the deadline, 724 words needed to be translated or revised per day.\n", + "There is a translation memory.\n", + "The project is currently delayed.\n", + "----\n", + "User Guide MFPs is a translation for UGent from EN into NL.\n", + "The translator is an agency collaborator and the reviewer is mm.\n", + "The domain is unspecified.\n", + "It's 1852 words long, with a rate of 0.4 € per word.\n", + "It started on 2023-04-12 and was due on 2023-04-15, so 3 days were foreseen for it. To meet the deadline, 864 words needed to be translated or revised per day.\n", + "There is a translation memory.\n", + "The project is currently cancelled.\n", + "----\n", + "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", + "The translator is evdl and the reviewer is sdw.\n", + "The domain is unspecified.\n", + "It's 11500 words long, with a rate of 0.35 € per word.\n", + "It started on 2023-05-06 and is due on 2023-06-30, so 55 days are foreseen for it, of which 12 left. To meet the deadline, 293 words need to be translated or revised per day.\n", + "There is no translation memory.\n", + "The project is currently in revision.\n", + "----\n" + ] + } + ], + "source": [ + "import json # we'll need the json module to use the file\n", + "# store the file's content in a variable:\n", + "projects = 'projects_db.json' # assign filename to a string variable\n", + "with open(projects, encoding = 'utf-8') as f:\n", + " # open file and use json to parse it\n", + " projects = json.load(f) # projects is now a list of dictionaries. \n", + "\n", + "# go through each of the items in the list\n", + "for project in projects:\n", + " # create a Project instance with title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status and domain\n", + " my_project = Project(project['title'], project['client'], project['source'], project['target'], project['words'], project['start'], project['deadline'], project['price'], project['tm'], project['translator'], project['reviewer'], project['status'], project['domain'])\n", + " # print the project information\n", + " print(my_project)\n", + " \n", + " # print a separating line between translations\n", + " print('----')" + ] + }, + { + "cell_type": "markdown", + "id": "acd1dcc3", + "metadata": {}, + "source": [ + "If we don't only want a printed overview of all the projects, but want to be able to retrieve information from specific attributes or use the other methods, we'll need to create objects of the `Project` class. Before we come to that, however, you might have noticed that instead of a name in Firstname Lastname format, freelancers are now referred to with abbreviations. Those are their references in the agency freelancer database and they are used to make the link between the `Project` and the `Freelancer` classes. So, let's first have a look at `Freelancer`." + ] + }, + { + "cell_type": "markdown", + "id": "35c2d175", + "metadata": {}, + "source": [ + "# Creating and using objects of the Freelancer class" + ] + }, + { + "cell_type": "markdown", + "id": "6a1897d3", + "metadata": {}, + "source": [ + "## Manually\n", + "While the class contents (described in `README.md`) differ from `Projects`, the `Freelancer` class works the same way. Objects can be instantiated manually, for example:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "a65c0362", + "metadata": {}, + "outputs": [], + "source": [ + "freelancer1 = Freelancer(\"Sibylle de Woot\", \"sdewoot@mail.be\", \"+32 486 12 34 56\", \"sdw\", [\"translator\", \"reviewer\"], [\"FR\", \"NL\", \"EN\", \"DE\"])" + ] + }, + { + "cell_type": "markdown", + "id": "11376c1a", + "metadata": {}, + "source": [ + "Again, you can then print a text displaying all the information:" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "b543aedf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sibylle de Woot can be contacted via e-mail (sdewoot@mail.be) or via phone (+32 486 12 34 56).\n", + "Sibylle de Woot's reference in our database is sdw.\n", + "Sibylle de Woot works as a translator, reviewer for FR, NL, EN, DE.\n" + ] + } + ], + "source": [ + "print(freelancer1)" + ] + }, + { + "cell_type": "markdown", + "id": "a7932f6b", + "metadata": {}, + "source": [ + "Or retrieve specific information through the attributes:" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "7e130be3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Sibylle de Woot'" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "freelancer1.name" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "b6851f59", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['FR', 'NL', 'EN', 'DE']" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "freelancer1.language" + ] + }, + { + "cell_type": "markdown", + "id": "181931df", + "metadata": {}, + "source": [ + "One limitation of the Freelancer class is its lack of validation. As a result, I can input virtually anything in any attribute: I could mix up information (for example exchange name and phone number), input the phone number as an integer rather than a string, not use the right format (for example not use the international version of the phone number), only give the freelancer's first name... Anything goes:" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "fb01bd9f", + "metadata": {}, + "outputs": [], + "source": [ + "freelancer2 = Freelancer(\"Sibylle\", 486123456, \"sdewoot@email.be\", \"sdw\", \"translator & reviewer\", \"French, Dutch, English and German\")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "ff4f4147", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sibylle can be contacted via e-mail (486123456) or via phone (sdewoot@email.be).\n", + "Sibylle's reference in our database is sdw.\n", + "Sibylle works as a t, r, a, n, s, l, a, t, o, r, , &, , r, e, v, i, e, w, e, r for F, r, e, n, c, h, ,, , D, u, t, c, h, ,, , E, n, g, l, i, s, h, , a, n, d, , G, e, r, m, a, n.\n" + ] + } + ], + "source": [ + "print(freelancer2)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "69e2ae3d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "486123456" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "freelancer2.email" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "5176310c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'French, Dutch, English and German'" + ] + }, + "execution_count": 29, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "freelancer2.language" + ] + }, + { + "cell_type": "markdown", + "id": "3f3c3675", + "metadata": {}, + "source": [ + "As you can see, the result is useless, unstructured and completely nonsensical. Hence why validation is a priority in the coming updates." + ] + }, + { + "cell_type": "markdown", + "id": "94b6a7a3", + "metadata": {}, + "source": [ + "## Using files\n", + "Like the `Project` class, the Freelancer class can go through a list (in this example, based on the file `freelancers_db.json`) and print each freelancer's information:" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "3e2a1e76", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Sibylle de Woot can be contacted via e-mail (sdewoot@email.be) or via phone (+32 485 12 34 56).\n", + "Sibylle de Woot's reference in our database is sdw.\n", + "Sibylle de Woot works as a translator, reviewer for FR, NL, EN, DE.\n", + "----\n", + "Mariana Montes can be contacted via e-mail (mariana.montes@company.com) or via phone (+32 487 98 76 54).\n", + "Mariana Montes's reference in our database is mm.\n", + "Mariana Montes works as a reviewer for ES, EN.\n", + "----\n", + "Emily van der Londen can be contacted via e-mail (evdl@translation.net) or via phone (+32 486 19 28 37).\n", + "Emily van der Londen's reference in our database is evdl.\n", + "Emily van der Londen works as a translator for NL, EN, FR.\n", + "----\n" + ] + } + ], + "source": [ + "freelancers = 'freelancers_db.json'\n", + "with open(freelancers, encoding = 'utf-8') as f:\n", + " freelancers = json.load(f)\n", + "\n", + "for freelancer in freelancers:\n", + " my_freelancer = Freelancer(freelancer['name'], freelancer['email'], freelancer['phone'], freelancer['ref'], freelancer['task'], freelancer['language'])\n", + " print(my_freelancer)\n", + " \n", + " print('----')" + ] + }, + { + "cell_type": "markdown", + "id": "6ca712c1", + "metadata": {}, + "source": [ + "But separate overviews of projects and freelancers aren't what interests us. What we're looking for, is to merge both in usable Python objects. So, let's see how that works." + ] + }, + { + "cell_type": "markdown", + "id": "19f9821c", + "metadata": {}, + "source": [ + "# Linking the Project and Freelancer class\n", + "For this, we need to slightly edit the `Project` class to make sure that the freelancer's name is displayed in the printed out project information, rather than the full text containing the freelancer information. We replace `{self.translator}` and `{self.reviewer}` by `{self.translator.name}` and `{self.reviewer.name}`." + ] + }, + { + "cell_type": "code", + "execution_count": 31, + "id": "0f76b171", + "metadata": {}, + "outputs": [], + "source": [ + "\"\"\"This script gives a translation agency an overview of all its projects.\n", + "\"\"\"\n", + "\n", + "class Project:\n", + "\n", + " def __init__(\n", + " self,\n", + " title,\n", + " client,\n", + " source,\n", + " target,\n", + " words,\n", + " start,\n", + " deadline,\n", + " price,\n", + " tm,\n", + " translator = 'internal',\n", + " reviewer = 'internal',\n", + " status = 'created',\n", + " domain = ''):\n", + " \"\"\"Initialises an object of the Project class, a class that represents a translation project of a translation agency.\n", + " \n", + " Args:\n", + " title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated).\n", + " client (str): Client who ordered the translation.\n", + " source (str): Language of the source document(s) (document(s) to be translated).\n", + " target (str): Language of the target document(s) (translation(s)).\n", + " words (int): Word count of the source document(s).\n", + " start (str): Project's start date in ISO format (YYYY-MM-DD).\n", + " deadline (str): Project's deadline in ISO format (YYYY-MM-DD).\n", + " price (float): Total price invoiced to the client (excl. VAT).\n", + " tm (bool): Whether a translation memory is available for this project.\n", + " translator (string, optional): Translator assigned to the project. Defaults to 'internal'.\n", + " reviewer (string, optional): Reviewer assigned to the project. Defaults to 'internal'.\n", + " status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'.\n", + " domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string.\n", + " \n", + " Attributes:\n", + " title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated).\n", + " client (str): Client who ordered the translation.\n", + " source (str): Language of the source document(s) (document(s) to be translated).\n", + " target (str): Language of the target document(s) (translation(s)).\n", + " words (int): Word count of the source document(s).\n", + " start (str): Project's start date in ISO format (YYYY-MM-DD).\n", + " deadline (str): Project's deadline in ISO format (YYYY-MM-DD).\n", + " price (float): Total price invoiced to the client (excl. VAT).\n", + " tm (bool): Whether a translation memory is available for this project.\n", + " translator (string, optional): Translator assigned to the project.\n", + " reviewer (string, optional): Reviewer assigned to the project.\n", + " status (string, optional): Current project status inside the agency's workflow.\n", + " domain (str): Overall domain to which the project belongs.\n", + " today (date): Date of the day where the script is run.\n", + " st (date): Project's start date, turned from an ISO-formatted string into a date.\n", + " dl (date): Project's deadline, turned from an ISO-formatted string into a date.\n", + " daysleft (timedelta): Time left until the project's deadline.\n", + " length (timedelta): Total time allocated to the project.\n", + " rate (float): Project's word rate.\n", + " efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek).\n", + " \n", + " Raises:\n", + " TypeError: If 'title' is not a string.\n", + " TypeError: If 'client' is not a string.\n", + " TypeError: If 'source' is not a string.\n", + " TypeError: If 'target' is not a string.\n", + " TypeError: If 'words' is not an integer.\n", + " TypeError: If 'start' is not a string.\n", + " ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits.\n", + " TypeError: If 'deadline' is not a string.\n", + " ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits.\n", + " TypeError: If 'price' is not a float.\n", + " TypeError: If 'tm' is not a boolean.\n", + " TypeError: If 'translator' is not a string.\n", + " TypeError: If 'reviewer' is not a string.\n", + " TypeError: If 'status' is not a string.\n", + " ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed.\n", + " TypeError: If 'domain' is not a string.\n", + " \"\"\"\n", + " if type(title) != str:\n", + " raise TypeError(\"The title must be a string.\")\n", + " else:\n", + " self.title = title\n", + " if type(client) != str:\n", + " raise TypeError(\"The client name must be a string.\")\n", + " else:\n", + " self.client = client\n", + " if type(source) != str:\n", + " raise TypeError(\"The source language must be a string.\")\n", + " else:\n", + " self.source = source\n", + " if type(target) != str:\n", + " raise TypeError(\"The target langague must be a string.\")\n", + " else:\n", + " self.target = target\n", + " if type(words) != int:\n", + " raise TypeError(\"The word count must be an integer.\")\n", + " else:\n", + " self.words = words\n", + " if type(start) != str:\n", + " raise TypeError(\"The start date must be provided as a string.\")\n", + " try:\n", + " self.st = datetime.date.fromisoformat(start)\n", + " except:\n", + " raise TypeError(\"The start date must be provided in ISO format\")\n", + " else:\n", + " self.start = start\n", + " if type(deadline) != str:\n", + " raise TypeError(\"The deadline must be provided as a string.\")\n", + " try:\n", + " self.dl = datetime.date.fromisoformat(deadline)\n", + " except:\n", + " raise TypeError(\"The deadline must be provided in ISO format\")\n", + " else:\n", + " self.deadline = deadline\n", + " if type(price) != float:\n", + " raise TypeError(\"The price must be a float.\")\n", + " else:\n", + " self.price = price\n", + " if type(tm) != bool:\n", + " raise TypeError(\"The TM availability must be a boolean.\")\n", + " else:\n", + " self.tm = tm\n", + " if type(translator) != str and type(translator) != Freelancer:\n", + " raise TypeError(\"The translator's name must be a string or an entry in our freelancer database.\")\n", + " elif translator == '':\n", + " self.translator = \"internal\"\n", + " else:\n", + " self.translator = translator\n", + " if type(reviewer) != str and type(reviewer) != Freelancer:\n", + " raise TypeError(\"The reviewer's name must be a string or an entry in our freelancer database.\")\n", + " elif reviewer == '':\n", + " self.reviewer = \"internal\"\n", + " else:\n", + " self.reviewer = reviewer\n", + " if type(status) != str:\n", + " raise TypeError(\"The status must be a string.\")\n", + " elif status == '':\n", + " self.status = \"created\"\n", + " elif not status.lower() in [\"created\",\n", + " \"in translation\",\n", + " \"in revision\",\n", + " \"delivered\",\n", + " \"delayed\",\n", + " \"cancelled\",\n", + " \"canceled\"]:\n", + " raise ValueError(\"Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.\")\n", + " else:\n", + " self.status = status\n", + " if type(domain) != str:\n", + " raise TypeError(\"The domain must be a string.\")\n", + " else:\n", + " self.domain = domain\n", + " \n", + " today = datetime.date.today()\n", + " self.daysleft = self.dl - today\n", + " self.length = self.dl - self.st\n", + " self.rate = self.price/self.words\n", + " self.efficiency = words/((5/7)*self.length.days)\n", + "\n", + " def days_left(self):\n", + " \"\"\"Displays how many days remain until the project deadline.\n", + " \n", + " Args:\n", + " None\n", + " \n", + " Returns:\n", + " str: A message informing the user that the deadline has been exceeded already if the deadline is in the past.\n", + " str: A message informing the user of the number of days left until the deadline if the deadline is not in the past.\n", + " \"\"\"\n", + " if self.dl < datetime.date.today():\n", + " return f\"The deadline has been exceeded already.\"\n", + " else:\n", + " return f\"There are {self.daysleft.days} days left until the deadline.\"\n", + " \n", + " def project_length(self):\n", + " \"\"\"Displays the total number of days allocated to the project.\n", + " \n", + " Args:\n", + " None\n", + " \n", + " Returns:\n", + " str: The total number of days allocated to the project.\n", + " \"\"\"\n", + " return f\"{self.length.days} days\"\n", + " \n", + " def __str__(self):\n", + " # defines the print behaviour: returns a text providing the main information about the project\n", + " sent_1 = f\"{self.title} is a translation for {self.client} from {self.source} into {self.target}.\"\n", + " if self.translator == \"internal\" and self.reviewer == \"internal\":\n", + " sent_2 = f\"Both the translator and the reviewer are agency collaborators.\"\n", + " elif self.translator == \"internal\" and self.reviewer != \"internal\":\n", + " sent_2 = f\"The translator is an agency collaborator and the reviewer is {self.reviewer.name}.\"\n", + " elif self.translator != \"internal\" and self.reviewer == \"internal\":\n", + " sent_2 = f\"The translator is {self.translator.name} and the reviewer is an agency collaborator.\"\n", + " else:\n", + " sent_2 = f\"The translator is {self.translator.name} and the reviewer is {self.reviewer.name}.\"\n", + " # this if-statement considers whether a domain was added\n", + " if len(self.domain) > 0:\n", + " sent_3 = f\"The domain is: {self.domain}.\"\n", + " else:\n", + " sent_3 = \"The domain is unspecified.\" # if no domain was added, the text mentions it\n", + " sent_4 = f\"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word.\" #the word rate is rounded to two decimal places to avoid cumbersomely long numbers\n", + " # this if-statement considers whether the deadline is in the past\n", + " if self.dl < datetime.date.today():\n", + " sent_5 = f\"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day.\" # the efficiency is rounded to units because you can't translate a fraction of a word anyway\n", + " else:\n", + " sent_5 = f\"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day.\"\n", + " # this if-statement considers whether there is a translation memory for the project\n", + " sent_6 = f\"There is {'a' if self.tm else 'no'} translation memory.\"\n", + " sent_7 = f\"The project is currently {self.status}.\"\n", + " # print each sentence in a different line\n", + " return \"\\n\".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7])" + ] + }, + { + "cell_type": "markdown", + "id": "0b55972d", + "metadata": {}, + "source": [ + "First, we'll retrieve the freelancers' information from the json-file and save it as a list of dictionaries. Granted, we've already done so above, but as some people may have skipped directly to this part of the tutorial, we'll do it again here." + ] + }, + { + "cell_type": "code", + "execution_count": 32, + "id": "1674b222", + "metadata": {}, + "outputs": [], + "source": [ + "with open('freelancers_db.json', 'r') as f:\n", + " all_freelancers = {x['ref'] : x for x in json.load(f)} # the name of each dictionary is the value of the key \"ref\", so the freelancer's unique internal reference in the agency database" + ] + }, + { + "cell_type": "markdown", + "id": "b9e75768", + "metadata": {}, + "source": [ + "Then, we also read and save the project information as a dictionary. As mentioned above, the value of the \"translator\" and \"reviewer\" key are now internal references rather than names, which we can use to check whether the value of those keys (or attributes, once we instantiates objects of the `Project` class) correspond to a dictionary in our list `all_freelancers` (i.e. an entry in our freelancer database). If it is, the value of the attribute `translator` of our `Project` instance will not be a string, but an instance of the `Freelancer` class." + ] + }, + { + "cell_type": "code", + "execution_count": 33, + "id": "825ceadd", + "metadata": {}, + "outputs": [], + "source": [ + "with open('guide_bruxelles.json', 'r') as f2:\n", + " guide_bruxelles_dict = json.load(f2)\n", + " if 'translator' in guide_bruxelles_dict and guide_bruxelles_dict['translator'] in all_freelancers:\n", + " guide_bruxelles_dict['translator'] = Freelancer(**all_freelancers[guide_bruxelles_dict['translator']])\n", + " if 'reviewer' in guide_bruxelles_dict and guide_bruxelles_dict['reviewer'] in all_freelancers:\n", + " guide_bruxelles_dict['reviewer'] = Freelancer(**all_freelancers[guide_bruxelles_dict['reviewer']])\n", + " guide_bruxelles = Project(**guide_bruxelles_dict)\n", + "\n", + "with open('handboek.json', 'r') as f3:\n", + " handboek_dict = json.load(f3)\n", + " if 'translator' in handboek_dict and handboek_dict['translator'] in all_freelancers:\n", + " handboek_dict['translator'] = Freelancer(**all_freelancers[handboek_dict['translator']])\n", + " if 'reviewer' in handboek_dict and handboek_dict['reviewer'] in all_freelancers:\n", + " handboek_dict['reviewer'] = Freelancer(**all_freelancers[handboek_dict['reviewer']])\n", + " handboek = Project(**handboek_dict)\n", + "\n", + "with open('rhumatismes_inflammatoires.json', 'r') as f4:\n", + " rhumatismes_inflammatoires_dict = json.load(f4)\n", + " if 'translator' in rhumatismes_inflammatoires_dict and rhumatismes_inflammatoires_dict['translator'] in all_freelancers:\n", + " rhumatismes_inflammatoires_dict['translator'] = Freelancer(**all_freelancers[rhumatismes_inflammatoires_dict['translator']])\n", + " if 'reviewer' in rhumatismes_inflammatoires_dict and rhumatismes_inflammatoires_dict['reviewer'] in all_freelancers:\n", + " rhumatismes_inflammatoires_dict['reviewer'] = Freelancer(**all_freelancers[rhumatismes_inflammatoires_dict['reviewer']])\n", + " rhumatismes_inflammatoires = Project(**rhumatismes_inflammatoires_dict)\n", + "\n", + "with open('user_guide.json', 'r') as f5:\n", + " user_guide_dict = json.load(f5)\n", + " if 'translator' in user_guide_dict and user_guide_dict['translator'] in all_freelancers:\n", + " user_guide_dict['translator'] = Freelancer(**all_freelancers[user_guide_dict['translator']])\n", + " if 'reviewer' in user_guide_dict and user_guide_dict['reviewer'] in all_freelancers:\n", + " user_guide_dict['reviewer'] = Freelancer(**all_freelancers[user_guide_dict['reviewer']])\n", + " user_guide = Project(**user_guide_dict)" + ] + }, + { + "cell_type": "markdown", + "id": "0032d9e8", + "metadata": {}, + "source": [ + "Once that is done, we can print both the project information:" + ] + }, + { + "cell_type": "code", + "execution_count": 34, + "id": "0d75f947", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Guide de Bruxelles is a translation for Foodies from NL into FR.\n", + "The translator is Emily van der Londen and the reviewer is Sibylle de Woot.\n", + "The domain is unspecified.\n", + "It's 11500 words long, with a rate of 0.35 € per word.\n", + "It started on 2023-05-06 and is due on 2023-06-30, so 55 days are foreseen for it, of which 12 left. To meet the deadline, 293 words need to be translated or revised per day.\n", + "There is no translation memory.\n", + "The project is currently in revision.\n", + "----\n", + "Handboek voor studentenvertegenwoordigers is a translation for KU Leuven from NL into EN.\n", + "The translator is Sibylle de Woot and the reviewer is an agency collaborator.\n", + "The domain is: education.\n", + "It's 7237 words long, with a rate of 0.37 € per word.\n", + "It started on 2023-02-21 and was due on 2023-03-07, so 14 days were foreseen for it. To meet the deadline, 724 words needed to be translated or revised per day.\n", + "There is a translation memory.\n", + "The project is currently delayed.\n", + "----\n", + "La polyarthrite rhumatoïde et autres rhumatismes inflammatoires is a translation for Reuma vzw from FR into NL.\n", + "Both the translator and the reviewer are agency collaborators.\n", + "The domain is: healthcare.\n", + "It's 2142 words long, with a rate of 0.33 € per word.\n", + "It started on 2020-10-02 and was due on 2020-10-15, so 13 days were foreseen for it. To meet the deadline, 231 words needed to be translated or revised per day.\n", + "There is no translation memory.\n", + "The project is currently delivered.\n", + "----\n", + "User Guide MFPs is a translation for UGent from EN into NL.\n", + "The translator is an agency collaborator and the reviewer is Mariana Montes.\n", + "The domain is unspecified.\n", + "It's 1852 words long, with a rate of 0.4 € per word.\n", + "It started on 2023-04-12 and was due on 2023-04-15, so 3 days were foreseen for it. To meet the deadline, 864 words needed to be translated or revised per day.\n", + "There is a translation memory.\n", + "The project is currently cancelled.\n" + ] + } + ], + "source": [ + "print(guide_bruxelles)\n", + "print('----')\n", + "print(handboek)\n", + "print('----')\n", + "print(rhumatismes_inflammatoires)\n", + "print('----')\n", + "print (user_guide)" + ] + }, + { + "cell_type": "markdown", + "id": "0d354535", + "metadata": {}, + "source": [ + "The freelancer information can then be printed separately by asking to print the value of the `translator`/`reviewer` attribute." + ] + }, + { + "cell_type": "code", + "execution_count": 35, + "id": "0250fbb3", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Emily van der Londen can be contacted via e-mail (evdl@translation.net) or via phone (+32 486 19 28 37).\n", + "Emily van der Londen's reference in our database is evdl.\n", + "Emily van der Londen works as a translator for NL, EN, FR.\n" + ] + } + ], + "source": [ + "print(guide_bruxelles.translator)" + ] + }, + { + "cell_type": "markdown", + "id": "14e0c7c7", + "metadata": {}, + "source": [ + "The value of other `Project` attributes can be retrieved the usual way:" + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "b0c41e69", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Guide de Bruxelles'" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guide_bruxelles.title" + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "07036a48", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "292.72727272727275" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guide_bruxelles.efficiency" + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "de05a880", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'55 days'" + ] + }, + "execution_count": 38, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "Project.project_length(guide_bruxelles)" + ] + }, + { + "cell_type": "markdown", + "id": "d822691f", + "metadata": {}, + "source": [ + "And the freelancers' information can also be retrieved through the `Project` instance:" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "f8cab93d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Emily van der Londen'" + ] + }, + "execution_count": 39, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guide_bruxelles.translator.name" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "cecba6bb", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'sdewoot@email.be'" + ] + }, + "execution_count": 40, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guide_bruxelles.reviewer.email" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "de2c768a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['translator']" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "guide_bruxelles.translator.task" + ] + }, + { + "cell_type": "markdown", + "id": "3d69f7cd", + "metadata": {}, + "source": [ + "# Conclusion\n", + "Hopefully, this script will make project management easier for many translation agencies. Of course, this is only version 1.0 and many updates, improvements and extra features are foreseen in the near to medium future, as you can see in the last section of `README.md`." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/project-management-tool_expanded.md b/project-management-tool_expanded.md new file mode 100644 index 0000000..f1aa7bc --- /dev/null +++ b/project-management-tool_expanded.md @@ -0,0 +1,1186 @@ +# The two classes +Below, you'll find the two classes described in `README.md`. Make their respective cells run to initialise the classes. + + +```python +import datetime + +"""This script gives a translation agency an overview of all its projects. +""" + +class Project: + + def __init__( + self, + title, + client, + source, + target, + words, + start, + deadline, + price, + tm, + translator = 'internal', + reviewer = 'internal', + status = 'created', + domain = ''): + """Initialises an object of the Project class, a class that represents a translation project of a translation agency. + + Args: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + translator (string, optional): Translator assigned to the project. Defaults to 'internal'. + reviewer (string, optional): Reviewer assigned to the project. Defaults to 'internal'. + status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'. + domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string. + + Attributes: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + translator (string, optional): Translator assigned to the project. + reviewer (string, optional): Reviewer assigned to the project. + status (string, optional): Current project status inside the agency's workflow. + domain (str): Overall domain to which the project belongs. + today (date): Date of the day where the script is run. + st (date): Project's start date, turned from an ISO-formatted string into a date. + dl (date): Project's deadline, turned from an ISO-formatted string into a date. + daysleft (timedelta): Time left until the project's deadline. + length (timedelta): Total time allocated to the project. + rate (float): Project's word rate. + efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek). + + Raises: + TypeError: If 'title' is not a string. + TypeError: If 'client' is not a string. + TypeError: If 'source' is not a string. + TypeError: If 'target' is not a string. + TypeError: If 'words' is not an integer. + TypeError: If 'start' is not a string. + ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'deadline' is not a string. + ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'price' is not a float. + TypeError: If 'tm' is not a boolean. + TypeError: If 'translator' is not a string. + TypeError: If 'reviewer' is not a string. + TypeError: If 'status' is not a string. + ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed. + TypeError: If 'domain' is not a string. + """ + if type(title) != str: + raise TypeError("The title must be a string.") + else: + self.title = title + if type(client) != str: + raise TypeError("The client name must be a string.") + else: + self.client = client + if type(source) != str: + raise TypeError("The source language must be a string.") + else: + self.source = source + if type(target) != str: + raise TypeError("The target langague must be a string.") + else: + self.target = target + if type(words) != int: + raise TypeError("The word count must be an integer.") + else: + self.words = words + if type(start) != str: + raise TypeError("The start date must be provided as a string.") + try: + self.st = datetime.date.fromisoformat(start) + except: + raise TypeError("The start date must be provided in ISO format") + else: + self.start = start + if type(deadline) != str: + raise TypeError("The deadline must be provided as a string.") + try: + self.dl = datetime.date.fromisoformat(deadline) + except: + raise TypeError("The deadline must be provided in ISO format") + else: + self.deadline = deadline + if type(price) != float: + raise TypeError("The price must be a float.") + else: + self.price = price + if type(tm) != bool: + raise TypeError("The TM availability must be a boolean.") + else: + self.tm = tm + if type(translator) != str and type(translator) != Freelancer: + raise TypeError("The translator's name must be a string or an entry in our freelancer database.") + elif translator == '': + self.translator = "internal" + else: + self.translator = translator + if type(reviewer) != str and type(reviewer) != Freelancer: + raise TypeError("The reviewer's name must be a string or an entry in our freelancer database.") + elif reviewer == '': + self.reviewer = "internal" + else: + self.reviewer = reviewer + if type(status) != str: + raise TypeError("The status must be a string.") + elif status == '': + self.status = "created" + elif not status.lower() in ["created", + "in translation", + "in revision", + "delivered", + "delayed", + "cancelled", + "canceled"]: + raise ValueError("Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.") + else: + self.status = status + if type(domain) != str: + raise TypeError("The domain must be a string.") + else: + self.domain = domain + + today = datetime.date.today() + self.daysleft = self.dl - today + self.length = self.dl - self.st + self.rate = self.price/self.words + self.efficiency = words/((5/7)*self.length.days) + + def days_left(self): + """Displays how many days remain until the project deadline. + + Args: + None + + Returns: + str: A message informing the user that the deadline has been exceeded already if the deadline is in the past. + str: A message informing the user of the number of days left until the deadline if the deadline is not in the past. + """ + if self.dl < datetime.date.today(): + return f"The deadline has been exceeded already." + else: + return f"There are {self.daysleft.days} days left until the deadline." + + def project_length(self): + """Displays the total number of days allocated to the project. + + Args: + None + + Returns: + str: The total number of days allocated to the project. + """ + return f"{self.length.days} days" + + def __str__(self): + # defines the print behaviour: returns a text providing the main information about the project + sent_1 = f"{self.title} is a translation for {self.client} from {self.source} into {self.target}." + if self.translator.lower() == "internal" and self.reviewer.lower() == "internal": + sent_2 = f"Both the translator and the reviewer are agency collaborators." + elif self.translator.lower() == "internal" and self.reviewer.lower() != "internal": + sent_2 = f"The translator is an agency collaborator and the reviewer is {self.reviewer}." + elif self.translator.lower() != "internal" and self.reviewer.lower() == "internal": + sent_2 = f"The translator is {self.translator} and the reviewer is an agency collaborator." + else: + sent_2 = f"The translator is {self.translator} and the reviewer is {self.reviewer}." + # this if-statement considers whether a domain was added + if len(self.domain) > 0: + sent_3 = f"The domain is: {self.domain}." + else: + sent_3 = "The domain is unspecified." # if no domain was added, the text mentions it + sent_4 = f"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word." #the word rate is rounded to two decimal places to avoid cumbersomely long numbers + # this if-statement considers whether the deadline is in the past + if self.dl < datetime.date.today(): + sent_5 = f"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day." # the efficiency is rounded to units because you can't translate a fraction of a word anyway + else: + sent_5 = f"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day." + # this if-statement considers whether there is a translation memory for the project + sent_6 = f"There is {'a' if self.tm else 'no'} translation memory." + sent_7 = f"The project is currently {self.status}." + # print each sentence in a different line + return "\n".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7]) +``` + + +```python +class Freelancer(): + + def __init__(self, name, email, phone, ref, task, language): + self.name = name + self.email = email + self.phone = phone + self.ref = ref + self.task = task + self.language = language + + def function_with_kwargs(**kwargs): + if "name" in kwargs: + print(kwargs["name"]) + else: + print("no name found") + + def __str__(self): + sent_1 = f"{self.name} can be contacted via e-mail ({self.email}) or via phone ({self.phone})." + sent_2 = f"{self.name}'s reference in our database is {self.ref}." + sent_3 = f"{self.name} works as a {', '.join(self.task)} for {', '.join(self.language)}." + return "\n".join([sent_1, sent_2, sent_3]) +``` + +# Creating and using objects of the Project class + +## Manually +It is possible to manually define objects of each class, meaning that you write out each mandatory attribute (and any optional attribute you want to make deviate from the default value), for example: + + +```python +project1 = Project("The project title", "some client", "FR", "EN", 10000, "2023-06-17", "2023-06-30", 3600.00, True) +``` + +In this case, the name of the object is "project1" and no optional attribute differs from the default value. But you can also change all the optional attributes: + + +```python +project2 = Project("A second project title", "another client", "NL", "DE", 7500, "2023-06-16", "2023-06-28", 3200.00, True, "Sibylle de Woot", "Mariana Montes", "in translation", "education") +``` + +Or only some of them: + + +```python +project3 = Project("Some other project title", "new client", "EN", "NL", 12000, "2023-06-15", "2023-06-30", 3800.00, True, reviewer = "Sibylle de Woot", domain = "healthcare") +``` + +Once an object has been created, you can use the various methods to +- Print an overview of all the project information: + + +```python +print(project1) +# The printing method is the only one that doesn't require you to specify that it's specific to the Project class, you simply need the name of the object +``` + + The project title is a translation for some client from FR into EN. + Both the translator and the reviewer are agency collaborators. + The domain is unspecified. + It's 10000 words long, with a rate of 0.36 € per word. + It started on 2023-06-17 and is due on 2023-06-30, so 13 days are foreseen for it, of which 12 left. To meet the deadline, 1077 words need to be translated or revised per day. + There is a translation memory. + The project is currently created. + + + +```python +print(project2) +``` + + A second project title is a translation for another client from NL into DE. + The translator is Sibylle de Woot and the reviewer is Mariana Montes. + The domain is: education. + It's 7500 words long, with a rate of 0.43 € per word. + It started on 2023-06-16 and is due on 2023-06-28, so 12 days are foreseen for it, of which 10 left. To meet the deadline, 875 words need to be translated or revised per day. + There is a translation memory. + The project is currently in translation. + + + +```python +print(project3) +``` + + Some other project title is a translation for new client from EN into NL. + The translator is an agency collaborator and the reviewer is Sibylle de Woot. + The domain is: healthcare. + It's 12000 words long, with a rate of 0.32 € per word. + It started on 2023-06-15 and is due on 2023-06-30, so 15 days are foreseen for it, of which 12 left. To meet the deadline, 1120 words need to be translated or revised per day. + There is a translation memory. + The project is currently created. + + +- Find out how much time is left until the deadline: + + +```python +Project.days_left(project1) +# To use any other class method, you need to use the format "Class.method(object)" +``` + + + + + 'There are 12 days left until the deadline.' + + + +- Find out how many days are foreseen in total for the project: + + +```python +Project.project_length(project1) +``` + + + + + '13 days' + + + +Attributes can also be retrieved separately, with `object.attribute`, for example + + +```python +project1.title +``` + + + + + 'The project title' + + + + +```python +project1.start +``` + + + + + '2023-06-17' + + + + +```python +project1.length +``` + + + + + datetime.timedelta(days=13) + + + +As you can see, the value of the attribute "length" isn't very user-friendly, as it is a timedelta computed with the datetime module. That is the reason why the method `project_length` was created, to display the attribute in a more readable way. Another user-friendely ways to display the project length is by printing the attribute, rather than simply retrieving it: + + +```python +print(project1.length) +``` + + 13 days, 0:00:00 + + +As you can see, the project length is calculated to the second here. For now, that's not useful, but when a future update allows PMs to specify the start and end time, it will be. + +So, feeding the project information into an object of the `Project` class makes for easy information retrieval and allows the PM to generate a clear global overview of the project in full sentences. However, especially with manual information input, issues can occur when the user makes a typo, or inputs the wrong type of variable. To counter those issues, validation was integrated into the `Project` class. Any wrong variable type raises an error, and the user is asked to use the right variable type. + +Say, for example, that I forget that the price needs to be a float (i.e. a decimal number): + + +```python +project4 = Project("Yet another title", "another client", "FR", "NL", 8000, "2023-06-16", "2023-06-28", 3300, True, "Sibylle de Woot", "Mariana Montes", "in translation", "technical") +``` + + + --------------------------------------------------------------------------- + + TypeError Traceback (most recent call last) + + Cell In[15], line 1 + ----> 1 project4 = Project("Yet another title", "another client", "FR", "NL", 8000, "2023-06-16", "2023-06-28", 3300, True, "Sibylle de Woot", "Mariana Montes", "in translation", "technical") + + + Cell In[1], line 117, in Project.__init__(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain) + 115 self.deadline = deadline + 116 if type(price) != float: + --> 117 raise TypeError("The price must be a float.") + 118 else: + 119 self.price = price + + + TypeError: The price must be a float. + + +As you can see, the script informs me that the price must be a float. Now, let's say that my numbers are correct, but that I didn't write my dates in ISO format: + + +```python +project4 = Project("Yet another title", "another client", "FR", "NL", 8000, "23-06-16", "23-06-28", 3300.00, True, "Sibylle de Woot", "Mariana Montes", "in translation", "technical") +``` + + + --------------------------------------------------------------------------- + + ValueError Traceback (most recent call last) + + Cell In[1], line 103, in Project.__init__(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain) + 102 try: + --> 103 self.st = datetime.date.fromisoformat(start) + 104 except: + + + ValueError: Invalid isoformat string: '23-06-16' + + + During handling of the above exception, another exception occurred: + + + TypeError Traceback (most recent call last) + + Cell In[16], line 1 + ----> 1 project4 = Project("Yet another title", "another client", "FR", "NL", 8000, "23-06-16", "23-06-28", 3300.00, True, "Sibylle de Woot", "Mariana Montes", "in translation", "technical") + + + Cell In[1], line 105, in Project.__init__(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain) + 103 self.st = datetime.date.fromisoformat(start) + 104 except: + --> 105 raise TypeError("The start date must be provided in ISO format") + 106 else: + 107 self.start = start + + + TypeError: The start date must be provided in ISO format + + +While both dates are wrong, only the error with the start date is raised. That's because the script stops at the first detected error. However, if I only correct one and not the other, it will raise the error in the deadline: + + +```python +project4 = Project("Yet another title", "another client", "FR", "NL", 8000, "2023-06-16", "23-06-28", 3300.00, True, "Sibylle de Woot", "Mariana Montes", "in translation", "technical") +``` + + + --------------------------------------------------------------------------- + + ValueError Traceback (most recent call last) + + Cell In[1], line 111, in Project.__init__(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain) + 110 try: + --> 111 self.dl = datetime.date.fromisoformat(deadline) + 112 except: + + + ValueError: Invalid isoformat string: '23-06-28' + + + During handling of the above exception, another exception occurred: + + + TypeError Traceback (most recent call last) + + Cell In[17], line 1 + ----> 1 project4 = Project("Yet another title", "another client", "FR", "NL", 8000, "2023-06-16", "23-06-28", 3300.00, True, "Sibylle de Woot", "Mariana Montes", "in translation", "technical") + + + Cell In[1], line 113, in Project.__init__(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain) + 111 self.dl = datetime.date.fromisoformat(deadline) + 112 except: + --> 113 raise TypeError("The deadline must be provided in ISO format") + 114 else: + 115 self.deadline = deadline + + + TypeError: The deadline must be provided in ISO format + + +The way the script detects whether or not the date format is correct, is by trying to turn the string I inputted into an actual date with the datetime module. Because if that, it can also catch some errors if I provide the date in YYYY-DD-MM format, rather than YYYY-MM-DD: + + +```python +project4 = Project("Yet another title", "another client", "FR", "NL", 8000, "2023-16-06", "2023-06-28", 3300.00, True, "Sibylle de Woot", "Mariana Montes", "in translation", "technical") +``` + + + --------------------------------------------------------------------------- + + ValueError Traceback (most recent call last) + + Cell In[1], line 103, in Project.__init__(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain) + 102 try: + --> 103 self.st = datetime.date.fromisoformat(start) + 104 except: + + + ValueError: month must be in 1..12 + + + During handling of the above exception, another exception occurred: + + + TypeError Traceback (most recent call last) + + Cell In[18], line 1 + ----> 1 project4 = Project("Yet another title", "another client", "FR", "NL", 8000, "2023-16-06", "2023-06-28", 3300.00, True, "Sibylle de Woot", "Mariana Montes", "in translation", "technical") + + + Cell In[1], line 105, in Project.__init__(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain) + 103 self.st = datetime.date.fromisoformat(start) + 104 except: + --> 105 raise TypeError("The start date must be provided in ISO format") + 106 else: + 107 self.start = start + + + TypeError: The start date must be provided in ISO format + + +When trying to convert the string into a date, the script runs into a problem, as there is no sixteenth month in the year, which leads it to ask for an ISO-formatted date. This is handy, but has its limitations for dates until the twelvth of the month, in which day and month can me inverted and still yield a valid ISO-formatted date. Validation is a useful feature to weed out many mistakes, but it's no miracle solution and in the end it's up to the user to ensure that they use correct information. + +A last "special" type of validation used in the `Project` class concerns the attribute `status`. To make sure everyone is on the same page concerning project progress, only labels from the agency workflow are allowed for this attribute, namely: +- created, +- in translation, +- in revision, +- delivered, +- delayed, +- cancel(l)ed. + +If a user tries to invent their own labels, they will receive a request to use a status in the agency workflow: + + +```python +project4 = Project("Yet another title", "another client", "FR", "NL", 8000, "2023-06-16", "2023-06-28", 3300.00, True, "Sibylle de Woot", "Mariana Montes", "ongoing", "technical") +``` + + + --------------------------------------------------------------------------- + + ValueError Traceback (most recent call last) + + Cell In[19], line 1 + ----> 1 project4 = Project("Yet another title", "another client", "FR", "NL", 8000, "2023-06-16", "2023-06-28", 3300.00, True, "Sibylle de Woot", "Mariana Montes", "ongoing", "technical") + + + Cell In[1], line 147, in Project.__init__(self, title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status, domain) + 139 self.status = "created" + 140 elif not status.lower() in ["created", + 141 "in translation", + 142 "in revision", + (...) + 145 "cancelled", + 146 "canceled"]: + --> 147 raise ValueError("Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.") + 148 else: + 149 self.status = status + + + ValueError: Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled. + + +A last point you may have noticed is that, while the default value when no domain is provided is an empty string, the default value for `translator` and `reviewer` is "internal" and even an empty string is turned into "internal", even though it does not appear as such in the printed text with project information (and an empty string can be turned into "an agency collaborator" just as easily as a string containing "internal"). The reason is that, if the user wants to retrieve the value of the attribute `translator`, "internal" is much clearer than an empty string, which looks like missing information and which requires more reflexion on the part of the user to understand that it's empty because the project was assigned to an agency collaborator. + + +```python +project1.translator +``` + + + + + 'internal' + + + +## Using files + +Since creating each instance of the `Project` class manually would be both cumbersome and *very* time-consuming, a way to speed up the process is to export the project information to a file and use that file to feed the information into the `Project` class. In this example, all the project information was exported as a list of dictionaries (one dictionary per project) in the json-file called `projects_db.json`. Bear in mind that, for the system to find the file, it needs to be in the folder you're running the script from (otherwise you need to use the full path to the file, but we'll not go into that here). + +If all you want is an overview of all the projects printed as text, you can go through the dictionaries one by one and print their information, without actually creating `Project` objects: + + +```python +import json # we'll need the json module to use the file +# store the file's content in a variable: +projects = 'projects_db.json' # assign filename to a string variable +with open(projects, encoding = 'utf-8') as f: + # open file and use json to parse it + projects = json.load(f) # projects is now a list of dictionaries. + +# go through each of the items in the list +for project in projects: + # create a Project instance with title, client, source, target, words, start, deadline, price, tm, translator, reviewer, status and domain + my_project = Project(project['title'], project['client'], project['source'], project['target'], project['words'], project['start'], project['deadline'], project['price'], project['tm'], project['translator'], project['reviewer'], project['status'], project['domain']) + # print the project information + print(my_project) + + # print a separating line between translations + print('----') +``` + + La polyarthrite rhumatoïde et autres rhumatismes inflammatoires is a translation for Reuma vzw from FR into NL. + Both the translator and the reviewer are agency collaborators. + The domain is: healthcare. + It's 2142 words long, with a rate of 0.33 € per word. + It started on 2020-10-02 and was due on 2020-10-15, so 13 days were foreseen for it. To meet the deadline, 231 words needed to be translated or revised per day. + There is no translation memory. + The project is currently delivered. + ---- + Handboek voor studentenvertegenwoordigers is a translation for KU Leuven from NL into EN. + The translator is sdw and the reviewer is an agency collaborator. + The domain is: education. + It's 7237 words long, with a rate of 0.37 € per word. + It started on 2023-02-21 and was due on 2023-03-07, so 14 days were foreseen for it. To meet the deadline, 724 words needed to be translated or revised per day. + There is a translation memory. + The project is currently delayed. + ---- + User Guide MFPs is a translation for UGent from EN into NL. + The translator is an agency collaborator and the reviewer is mm. + The domain is unspecified. + It's 1852 words long, with a rate of 0.4 € per word. + It started on 2023-04-12 and was due on 2023-04-15, so 3 days were foreseen for it. To meet the deadline, 864 words needed to be translated or revised per day. + There is a translation memory. + The project is currently cancelled. + ---- + Guide de Bruxelles is a translation for Foodies from NL into FR. + The translator is evdl and the reviewer is sdw. + The domain is unspecified. + It's 11500 words long, with a rate of 0.35 € per word. + It started on 2023-05-06 and is due on 2023-06-30, so 55 days are foreseen for it, of which 12 left. To meet the deadline, 293 words need to be translated or revised per day. + There is no translation memory. + The project is currently in revision. + ---- + + +If we don't only want a printed overview of all the projects, but want to be able to retrieve information from specific attributes or use the other methods, we'll need to create objects of the `Project` class. Before we come to that, however, you might have noticed that instead of a name in Firstname Lastname format, freelancers are now referred to with abbreviations. Those are their references in the agency freelancer database and they are used to make the link between the `Project` and the `Freelancer` classes. So, let's first have a look at `Freelancer`. + +# Creating and using objects of the Freelancer class + +## Manually +While the class contents (described in `README.md`) differ from `Projects`, the `Freelancer` class works the same way. Objects can be instantiated manually, for example: + + +```python +freelancer1 = Freelancer("Sibylle de Woot", "sdewoot@mail.be", "+32 486 12 34 56", "sdw", ["translator", "reviewer"], ["FR", "NL", "EN", "DE"]) +``` + +Again, you can then print a text displaying all the information: + + +```python +print(freelancer1) +``` + + Sibylle de Woot can be contacted via e-mail (sdewoot@mail.be) or via phone (+32 486 12 34 56). + Sibylle de Woot's reference in our database is sdw. + Sibylle de Woot works as a translator, reviewer for FR, NL, EN, DE. + + +Or retrieve specific information through the attributes: + + +```python +freelancer1.name +``` + + + + + 'Sibylle de Woot' + + + + +```python +freelancer1.language +``` + + + + + ['FR', 'NL', 'EN', 'DE'] + + + +One limitation of the Freelancer class is its lack of validation. As a result, I can input virtually anything in any attribute: I could mix up information (for example exchange name and phone number), input the phone number as an integer rather than a string, not use the right format (for example not use the international version of the phone number), only give the freelancer's first name... Anything goes: + + +```python +freelancer2 = Freelancer("Sibylle", 486123456, "sdewoot@email.be", "sdw", "translator & reviewer", "French, Dutch, English and German") +``` + + +```python +print(freelancer2) +``` + + Sibylle can be contacted via e-mail (486123456) or via phone (sdewoot@email.be). + Sibylle's reference in our database is sdw. + Sibylle works as a t, r, a, n, s, l, a, t, o, r, , &, , r, e, v, i, e, w, e, r for F, r, e, n, c, h, ,, , D, u, t, c, h, ,, , E, n, g, l, i, s, h, , a, n, d, , G, e, r, m, a, n. + + + +```python +freelancer2.email +``` + + + + + 486123456 + + + + +```python +freelancer2.language +``` + + + + + 'French, Dutch, English and German' + + + +As you can see, the result is useless, unstructured and completely nonsensical. Hence why validation is a priority in the coming updates. + +## Using files +Like the `Project` class, the Freelancer class can go through a list (in this example, based on the file `freelancers_db.json`) and print each freelancer's information: + + +```python +freelancers = 'freelancers_db.json' +with open(freelancers, encoding = 'utf-8') as f: + freelancers = json.load(f) + +for freelancer in freelancers: + my_freelancer = Freelancer(freelancer['name'], freelancer['email'], freelancer['phone'], freelancer['ref'], freelancer['task'], freelancer['language']) + print(my_freelancer) + + print('----') +``` + + Sibylle de Woot can be contacted via e-mail (sdewoot@email.be) or via phone (+32 485 12 34 56). + Sibylle de Woot's reference in our database is sdw. + Sibylle de Woot works as a translator, reviewer for FR, NL, EN, DE. + ---- + Mariana Montes can be contacted via e-mail (mariana.montes@company.com) or via phone (+32 487 98 76 54). + Mariana Montes's reference in our database is mm. + Mariana Montes works as a reviewer for ES, EN. + ---- + Emily van der Londen can be contacted via e-mail (evdl@translation.net) or via phone (+32 486 19 28 37). + Emily van der Londen's reference in our database is evdl. + Emily van der Londen works as a translator for NL, EN, FR. + ---- + + +But separate overviews of projects and freelancers aren't what interests us. What we're looking for, is to merge both in usable Python objects. So, let's see how that works. + +# Linking the Project and Freelancer class +For this, we need to slightly edit the `Project` class to make sure that the freelancer's name is displayed in the printed out project information, rather than the full text containing the freelancer information. We replace `{self.translator}` and `{self.reviewer}` by `{self.translator.name}` and `{self.reviewer.name}`. + + +```python +"""This script gives a translation agency an overview of all its projects. +""" + +class Project: + + def __init__( + self, + title, + client, + source, + target, + words, + start, + deadline, + price, + tm, + translator = 'internal', + reviewer = 'internal', + status = 'created', + domain = ''): + """Initialises an object of the Project class, a class that represents a translation project of a translation agency. + + Args: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + translator (string, optional): Translator assigned to the project. Defaults to 'internal'. + reviewer (string, optional): Reviewer assigned to the project. Defaults to 'internal'. + status (string, optional): Current project status inside the agency's workflow. Defaults to 'created'. + domain (str, optional): Overall domain to which the project belongs. Defaults to an empty string. + + Attributes: + title (str): Title of the project (typically the title of the source document, or the overall title the agency gave the project if there's more than one document to be translated). + client (str): Client who ordered the translation. + source (str): Language of the source document(s) (document(s) to be translated). + target (str): Language of the target document(s) (translation(s)). + words (int): Word count of the source document(s). + start (str): Project's start date in ISO format (YYYY-MM-DD). + deadline (str): Project's deadline in ISO format (YYYY-MM-DD). + price (float): Total price invoiced to the client (excl. VAT). + tm (bool): Whether a translation memory is available for this project. + translator (string, optional): Translator assigned to the project. + reviewer (string, optional): Reviewer assigned to the project. + status (string, optional): Current project status inside the agency's workflow. + domain (str): Overall domain to which the project belongs. + today (date): Date of the day where the script is run. + st (date): Project's start date, turned from an ISO-formatted string into a date. + dl (date): Project's deadline, turned from an ISO-formatted string into a date. + daysleft (timedelta): Time left until the project's deadline. + length (timedelta): Total time allocated to the project. + rate (float): Project's word rate. + efficiency (float): Number of words to be translated or revised per day to meet the deadline (assuming a five-day workweek). + + Raises: + TypeError: If 'title' is not a string. + TypeError: If 'client' is not a string. + TypeError: If 'source' is not a string. + TypeError: If 'target' is not a string. + TypeError: If 'words' is not an integer. + TypeError: If 'start' is not a string. + ValueError: If 'start' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'deadline' is not a string. + ValueError: If 'deadline' does not follow the pattern 4 digits-2 digits-2 digits. + TypeError: If 'price' is not a float. + TypeError: If 'tm' is not a boolean. + TypeError: If 'translator' is not a string. + TypeError: If 'reviewer' is not a string. + TypeError: If 'status' is not a string. + ValueError: If 'status' is not a label in the agency workflow: created, in translation, in revision, delivered, delayed, cancel(l)ed. + TypeError: If 'domain' is not a string. + """ + if type(title) != str: + raise TypeError("The title must be a string.") + else: + self.title = title + if type(client) != str: + raise TypeError("The client name must be a string.") + else: + self.client = client + if type(source) != str: + raise TypeError("The source language must be a string.") + else: + self.source = source + if type(target) != str: + raise TypeError("The target langague must be a string.") + else: + self.target = target + if type(words) != int: + raise TypeError("The word count must be an integer.") + else: + self.words = words + if type(start) != str: + raise TypeError("The start date must be provided as a string.") + try: + self.st = datetime.date.fromisoformat(start) + except: + raise TypeError("The start date must be provided in ISO format") + else: + self.start = start + if type(deadline) != str: + raise TypeError("The deadline must be provided as a string.") + try: + self.dl = datetime.date.fromisoformat(deadline) + except: + raise TypeError("The deadline must be provided in ISO format") + else: + self.deadline = deadline + if type(price) != float: + raise TypeError("The price must be a float.") + else: + self.price = price + if type(tm) != bool: + raise TypeError("The TM availability must be a boolean.") + else: + self.tm = tm + if type(translator) != str and type(translator) != Freelancer: + raise TypeError("The translator's name must be a string or an entry in our freelancer database.") + elif translator == '': + self.translator = "internal" + else: + self.translator = translator + if type(reviewer) != str and type(reviewer) != Freelancer: + raise TypeError("The reviewer's name must be a string or an entry in our freelancer database.") + elif reviewer == '': + self.reviewer = "internal" + else: + self.reviewer = reviewer + if type(status) != str: + raise TypeError("The status must be a string.") + elif status == '': + self.status = "created" + elif not status.lower() in ["created", + "in translation", + "in revision", + "delivered", + "delayed", + "cancelled", + "canceled"]: + raise ValueError("Please pick a status from the workflow: created, in translation, in revision, delivered, delayed or cancelled.") + else: + self.status = status + if type(domain) != str: + raise TypeError("The domain must be a string.") + else: + self.domain = domain + + today = datetime.date.today() + self.daysleft = self.dl - today + self.length = self.dl - self.st + self.rate = self.price/self.words + self.efficiency = words/((5/7)*self.length.days) + + def days_left(self): + """Displays how many days remain until the project deadline. + + Args: + None + + Returns: + str: A message informing the user that the deadline has been exceeded already if the deadline is in the past. + str: A message informing the user of the number of days left until the deadline if the deadline is not in the past. + """ + if self.dl < datetime.date.today(): + return f"The deadline has been exceeded already." + else: + return f"There are {self.daysleft.days} days left until the deadline." + + def project_length(self): + """Displays the total number of days allocated to the project. + + Args: + None + + Returns: + str: The total number of days allocated to the project. + """ + return f"{self.length.days} days" + + def __str__(self): + # defines the print behaviour: returns a text providing the main information about the project + sent_1 = f"{self.title} is a translation for {self.client} from {self.source} into {self.target}." + if self.translator == "internal" and self.reviewer == "internal": + sent_2 = f"Both the translator and the reviewer are agency collaborators." + elif self.translator == "internal" and self.reviewer != "internal": + sent_2 = f"The translator is an agency collaborator and the reviewer is {self.reviewer.name}." + elif self.translator != "internal" and self.reviewer == "internal": + sent_2 = f"The translator is {self.translator.name} and the reviewer is an agency collaborator." + else: + sent_2 = f"The translator is {self.translator.name} and the reviewer is {self.reviewer.name}." + # this if-statement considers whether a domain was added + if len(self.domain) > 0: + sent_3 = f"The domain is: {self.domain}." + else: + sent_3 = "The domain is unspecified." # if no domain was added, the text mentions it + sent_4 = f"It's {self.words} words long, with a rate of {round(self.rate, 2)} € per word." #the word rate is rounded to two decimal places to avoid cumbersomely long numbers + # this if-statement considers whether the deadline is in the past + if self.dl < datetime.date.today(): + sent_5 = f"It started on {self.st} and was due on {self.dl}, so {self.length.days} days were foreseen for it. To meet the deadline, {round(self.efficiency)} words needed to be translated or revised per day." # the efficiency is rounded to units because you can't translate a fraction of a word anyway + else: + sent_5 = f"It started on {self.st} and is due on {self.dl}, so {self.length.days} days are foreseen for it, of which {self.daysleft.days} left. To meet the deadline, {round(self.efficiency)} words need to be translated or revised per day." + # this if-statement considers whether there is a translation memory for the project + sent_6 = f"There is {'a' if self.tm else 'no'} translation memory." + sent_7 = f"The project is currently {self.status}." + # print each sentence in a different line + return "\n".join([sent_1, sent_2, sent_3, sent_4, sent_5, sent_6, sent_7]) +``` + +First, we'll retrieve the freelancers' information from the json-file and save it as a list of dictionaries. Granted, we've already done so above, but as some people may have skipped directly to this part of the tutorial, we'll do it again here. + + +```python +with open('freelancers_db.json', 'r') as f: + all_freelancers = {x['ref'] : x for x in json.load(f)} # the name of each dictionary is the value of the key "ref", so the freelancer's unique internal reference in the agency database +``` + +Then, we also read and save the project information as a dictionary. As mentioned above, the value of the "translator" and "reviewer" key are now internal references rather than names, which we can use to check whether the value of those keys (or attributes, once we instantiates objects of the `Project` class) correspond to a dictionary in our list `all_freelancers` (i.e. an entry in our freelancer database). If it is, the value of the attribute `translator` of our `Project` instance will not be a string, but an instance of the `Freelancer` class. + + +```python +with open('guide_bruxelles.json', 'r') as f2: + guide_bruxelles_dict = json.load(f2) + if 'translator' in guide_bruxelles_dict and guide_bruxelles_dict['translator'] in all_freelancers: + guide_bruxelles_dict['translator'] = Freelancer(**all_freelancers[guide_bruxelles_dict['translator']]) + if 'reviewer' in guide_bruxelles_dict and guide_bruxelles_dict['reviewer'] in all_freelancers: + guide_bruxelles_dict['reviewer'] = Freelancer(**all_freelancers[guide_bruxelles_dict['reviewer']]) + guide_bruxelles = Project(**guide_bruxelles_dict) + +with open('handboek.json', 'r') as f3: + handboek_dict = json.load(f3) + if 'translator' in handboek_dict and handboek_dict['translator'] in all_freelancers: + handboek_dict['translator'] = Freelancer(**all_freelancers[handboek_dict['translator']]) + if 'reviewer' in handboek_dict and handboek_dict['reviewer'] in all_freelancers: + handboek_dict['reviewer'] = Freelancer(**all_freelancers[handboek_dict['reviewer']]) + handboek = Project(**handboek_dict) + +with open('rhumatismes_inflammatoires.json', 'r') as f4: + rhumatismes_inflammatoires_dict = json.load(f4) + if 'translator' in rhumatismes_inflammatoires_dict and rhumatismes_inflammatoires_dict['translator'] in all_freelancers: + rhumatismes_inflammatoires_dict['translator'] = Freelancer(**all_freelancers[rhumatismes_inflammatoires_dict['translator']]) + if 'reviewer' in rhumatismes_inflammatoires_dict and rhumatismes_inflammatoires_dict['reviewer'] in all_freelancers: + rhumatismes_inflammatoires_dict['reviewer'] = Freelancer(**all_freelancers[rhumatismes_inflammatoires_dict['reviewer']]) + rhumatismes_inflammatoires = Project(**rhumatismes_inflammatoires_dict) + +with open('user_guide.json', 'r') as f5: + user_guide_dict = json.load(f5) + if 'translator' in user_guide_dict and user_guide_dict['translator'] in all_freelancers: + user_guide_dict['translator'] = Freelancer(**all_freelancers[user_guide_dict['translator']]) + if 'reviewer' in user_guide_dict and user_guide_dict['reviewer'] in all_freelancers: + user_guide_dict['reviewer'] = Freelancer(**all_freelancers[user_guide_dict['reviewer']]) + user_guide = Project(**user_guide_dict) +``` + +Once that is done, we can print both the project information: + + +```python +print(guide_bruxelles) +print('----') +print(handboek) +print('----') +print(rhumatismes_inflammatoires) +print('----') +print (user_guide) +``` + + Guide de Bruxelles is a translation for Foodies from NL into FR. + The translator is Emily van der Londen and the reviewer is Sibylle de Woot. + The domain is unspecified. + It's 11500 words long, with a rate of 0.35 € per word. + It started on 2023-05-06 and is due on 2023-06-30, so 55 days are foreseen for it, of which 12 left. To meet the deadline, 293 words need to be translated or revised per day. + There is no translation memory. + The project is currently in revision. + ---- + Handboek voor studentenvertegenwoordigers is a translation for KU Leuven from NL into EN. + The translator is Sibylle de Woot and the reviewer is an agency collaborator. + The domain is: education. + It's 7237 words long, with a rate of 0.37 € per word. + It started on 2023-02-21 and was due on 2023-03-07, so 14 days were foreseen for it. To meet the deadline, 724 words needed to be translated or revised per day. + There is a translation memory. + The project is currently delayed. + ---- + La polyarthrite rhumatoïde et autres rhumatismes inflammatoires is a translation for Reuma vzw from FR into NL. + Both the translator and the reviewer are agency collaborators. + The domain is: healthcare. + It's 2142 words long, with a rate of 0.33 € per word. + It started on 2020-10-02 and was due on 2020-10-15, so 13 days were foreseen for it. To meet the deadline, 231 words needed to be translated or revised per day. + There is no translation memory. + The project is currently delivered. + ---- + User Guide MFPs is a translation for UGent from EN into NL. + The translator is an agency collaborator and the reviewer is Mariana Montes. + The domain is unspecified. + It's 1852 words long, with a rate of 0.4 € per word. + It started on 2023-04-12 and was due on 2023-04-15, so 3 days were foreseen for it. To meet the deadline, 864 words needed to be translated or revised per day. + There is a translation memory. + The project is currently cancelled. + + +The freelancer information can then be printed separately by asking to print the value of the `translator`/`reviewer` attribute. + + +```python +print(guide_bruxelles.translator) +``` + + Emily van der Londen can be contacted via e-mail (evdl@translation.net) or via phone (+32 486 19 28 37). + Emily van der Londen's reference in our database is evdl. + Emily van der Londen works as a translator for NL, EN, FR. + + +The value of other `Project` attributes can be retrieved the usual way: + + +```python +guide_bruxelles.title +``` + + + + + 'Guide de Bruxelles' + + + + +```python +guide_bruxelles.efficiency +``` + + + + + 292.72727272727275 + + + + +```python +Project.project_length(guide_bruxelles) +``` + + + + + '55 days' + + + +And the freelancers' information can also be retrieved through the `Project` instance: + + +```python +guide_bruxelles.translator.name +``` + + + + + 'Emily van der Londen' + + + + +```python +guide_bruxelles.reviewer.email +``` + + + + + 'sdewoot@email.be' + + + + +```python +guide_bruxelles.translator.task +``` + + + + + ['translator'] + + + +# Conclusion +Hopefully, this script will make project management easier for many translation agencies. Of course, this is only version 1.0 and many updates, improvements and extra features are foreseen in the near to medium future, as you can see in the last section of `README.md`. diff --git a/projects_db.json b/projects_db.json new file mode 100644 index 0000000..763aadd --- /dev/null +++ b/projects_db.json @@ -0,0 +1 @@ +[{"title": "La polyarthrite rhumato\u00efde et autres rhumatismes inflammatoires", "client": "Reuma vzw", "source": "FR", "target": "NL", "words": 2142, "start": "2020-10-02", "deadline": "2020-10-15", "price": 715.0, "tm": false, "translator": "", "reviewer": "", "status": "delivered", "domain": "healthcare"}, {"title": "Handboek voor studentenvertegenwoordigers", "client": "KU Leuven", "source": "NL", "target": "EN", "words": 7237, "start": "2023-02-21", "deadline": "2023-03-07", "price": 2680.0, "tm": true, "translator": "sdw", "reviewer": "", "status": "delayed", "domain": "education"}, {"title": "User Guide MFPs", "client": "UGent", "source": "EN", "target": "NL", "words": 1852, "start": "2023-04-12", "deadline": "2023-04-15", "price": 740.0, "tm": true, "translator": "", "reviewer": "mm", "status": "cancelled", "domain": ""}, {"title": "Guide de Bruxelles", "client": "Foodies", "source": "NL", "target": "FR", "words": 11500, "start": "2023-05-06", "deadline": "2023-06-30", "price": 4025.0, "tm": false, "translator": "evdl", "reviewer": "sdw", "status": "in revision", "domain": ""}] \ No newline at end of file diff --git a/python_en.txt b/python_en.txt new file mode 100644 index 0000000..93484cf --- /dev/null +++ b/python_en.txt @@ -0,0 +1 @@ +Introduction to Machine Learning with Python. This module provides an introduction to the basic concepts and use of the Python programming language in support of translation. Focus lies on the main concepts that include Natural Language Processing, automation, text analysis and machine learning. \ No newline at end of file diff --git a/python_fr.txt b/python_fr.txt new file mode 100644 index 0000000..c969aa6 --- /dev/null +++ b/python_fr.txt @@ -0,0 +1 @@ +Introduction au machine learning à l’aide de Python. Ce module offre une introduction aux concepts de base et à l’utilisation du langage de programmation Python comme aide à la traduction. L’accent est mis sur le traitement du langage naturel (NLP), l’automatisation, l’analyse de texte et le machine learning. \ No newline at end of file diff --git a/rhumatismes_inflammatoires.json b/rhumatismes_inflammatoires.json new file mode 100644 index 0000000..e7f81d9 --- /dev/null +++ b/rhumatismes_inflammatoires.json @@ -0,0 +1 @@ +{"title": "La polyarthrite rhumato\u00efde et autres rhumatismes inflammatoires", "client": "Reuma vzw", "source": "FR", "target": "NL", "words": 2142, "start": "2020-10-02", "deadline": "2020-10-15", "price": 715.0, "tm": false, "translator": "", "reviewer": "", "status": "delivered", "domain": "healthcare"} \ No newline at end of file diff --git a/user_guide.json b/user_guide.json new file mode 100644 index 0000000..f8bd4ee --- /dev/null +++ b/user_guide.json @@ -0,0 +1 @@ +{"title": "User Guide MFPs", "client": "UGent", "source": "EN", "target": "NL", "words": 1852, "start": "2023-04-12", "deadline": "2023-04-15", "price": 740.0, "tm": true, "translator": "", "reviewer": "mm", "status": "cancelled", "domain": ""} \ No newline at end of file From dabaa0700b5d91dae6eaf757c0ae997e4df66fe0 Mon Sep 17 00:00:00 2001 From: Sib007 <132289113+Sib007@users.noreply.github.com> Date: Sun, 18 Jun 2023 11:00:24 +0200 Subject: [PATCH 46/46] Deleting superfluous file --- README.ipynb | 193 --------------------------------------------------- 1 file changed, 193 deletions(-) delete mode 100644 README.ipynb diff --git a/README.ipynb b/README.ipynb deleted file mode 100644 index 253343c..0000000 --- a/README.ipynb +++ /dev/null @@ -1,193 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "0b6e787b", - "metadata": {}, - "source": [ - "# Project management tool for a translation agency" - ] - }, - { - "cell_type": "markdown", - "id": "13819178", - "metadata": {}, - "source": [ - "## Introduction\n", - "Every translation agency needs to keep track of its past and ongoing projects, just like any company. However, when projects start to accumulate, it may become rather difficult to keep an overview. In addition, large tables or endless database entries do not make for comfortable reading. This tool enables project managers to quickly get an overview of their translation projects and all their information, as a user-friendly and informative text, as well as retrieving specific information." - ] - }, - { - "cell_type": "markdown", - "id": "57f1c0ca", - "metadata": {}, - "source": [ - "## Files in this repository\n", - "- `README.md` (this file): Gives an overview of the repository, the files it contains and how to use them.\n", - "- `project-management-tool_basic.py`: A basic version of the project management tool, which can be run from the command line rather than importing it to use it.\n", - "- `project-management-tool_expanded.ipynb`: An interactive version of the script, with more features than the basic version, but which needs to be imported and opened and cannot be run from the command line.\n", - "- `project-management-tool_expanded.md`: The markdown version of the file above.\n", - "- `projects_db.json`: A sample project database, containing a list of dictionaries with all the information for each project, namely:\n", - " - `title` (a string) indicates the project's title (typically the title of the source document, or the overall title the translator gave the project if there's more than one document to be translated);\n", - " - `client` (a string) indicates the client who ordered the translation;\n", - " - `source` (a string) indicates the language of the source document (document to be translated);\n", - " - `target` (a string) indicates the language of the target document (translation);\n", - " - `words` (an integer) indicates the word count of the source document;\n", - " - `start` (a string) indicates the project's start date in ISO format (YYYY-MM-DD);\n", - " - `deadline` (a string) indicates the project's deadline in ISO format (YYYY-MM-DD);\n", - " - `price` (a float) indicates the total price invoiced to the client (excl. VAT);\n", - " - `tm` (a boolean) indicates whether or not a translation memory is available for this project;\n", - " - `translator` (a string) indicates the freelancer assigned to translate this project (using their unique reference inside the agency's database) or is left empty to indicate that an agency collaborator translated the project;\n", - " - `reviewer` (a string) indicates the freelancer assigned to review this project (using their unique reference inside the agency's database) or is left empty to indicate that an agency collaborator reviewed the project;\n", - " - `status` (a string) indicates the project's status in the agency workflow;\n", - " - `domain` (a string) indicates the overall domain to which the project belongs.\n", - "- `guide_bruxelles.json`, `handboek.json`, `rhumatismes_inflammatoires.json` and `user_guide.json`: Separate files containing the same information as `projects_db.json`, but with one file per project.\n", - "- `freelancers_db.json`: A sample database (again, as a list of dictionaries), with an overview of three freelancers' information, namely:\n", - " - `name` (a string): The freelancer's name in Firstname Lastname format;\n", - " - `email`(a string): The freelancer's email address;\n", - " - `phone`(a string): The freelancer's phone number (international version, so starting with \"+\");\n", - " - `task` (a set of strings): The task(s) the freelancer usually performs for the agency, namely whether they're a translator or a reviewer;\n", - " - `language`(a set of strings): The freelancer's working language(s). \n", - "- `add-on_database-generator.ipynb` and `add-on_database-generator.md`: An add-on not integrated in the project management tool itself, but which you can use to generate the freelancer/project information in the dictionary format you may need for the script (i.e. same format as the json-files mentioned above).\n", - "- `add-on_TM-generator.ipynb` and `add-on_TM-generator.md`: An add-on not integrated in the project management tool itself, but which you can use to generate a translation memory based on a source and target text, so you can turn your project information from `TM = false` into `TM = true`.\n", - "- `python_en.txt` and `python_en.txt`: A sample source and target text to try out the TM generator." - ] - }, - { - "cell_type": "markdown", - "id": "c103f3f4", - "metadata": {}, - "source": [ - "## Composition of the project management tool" - ] - }, - { - "cell_type": "markdown", - "id": "eb04b1a9", - "metadata": {}, - "source": [ - "### Basic version\n", - "The tool consists of a Python class called `Project`. That class creates objects with the following **attributes**:\n", - "- 13 instance attributes, which are the same as dictionary keys in the project information dictionaries. The last four attributes are optional and have the following default values:\n", - " - `translator`: \"internal\", meaning that an agency collaborator was assigned to the project in this role;\n", - " - `reviewer`: \"internal\", meaning that an agency collaborator was assigned to the project in this role;\n", - " - `status`: \"created\";\n", - " - `domain`: An empty string, meaning that no domain was provided for this project (so it's probably a general text).\n", - "- 7 computed attributes, namely:\n", - " - `st`: A conversion of the start date from a string into an ISO-formatted date using the `datetime` module.\n", - " - `dl`: A conversion of the deadline from a string into an ISO-formatted date using the `datetime` module.\n", - " - `today`: The date of the day on which the tool is used, computed using the `datetime` module.\n", - " - `daysleft`: The number of days left until the project deadline, computed by substracting the current date (`today`) from the project deadline (`dl`).\n", - " - `length`: The total number of days foreseen for the project (weekends and holidays included), computed by subtracting the start date (`st`) from the deadline (`dl`).\n", - " - `rate`: The rate per word the client pays for the project, computed by dividing the project price by the word count.\n", - " - `efficiency`: The number of words to translate or revise per work day to meet the deadline, computed by dividing the word count by 5/7th of the total project length. Does not take into account holidays.\n", - "\n", - "The class also has three **methods**:\n", - "- `days_left`: Prints a message indicating the number of days left until the project deadline if the deadline is in the past or present, and indicating that the deadline has been exceeded already if it is in the past.\n", - "- `project_length`: Prints the total number of days (weekends included) foreseen for the project (as calling that attribute would otherwise return a datetime timedelta, which isn't very clear for most users).\n", - "- a `printing method`, which displays the project information using the following text template:\n", - " - \"`title` is a translation for`client` from `source` into `target`.\n", - " - Both the translator and the reviewer are agency collaborators. (Or \"The translator is `translator` and the reviewer is `reviewer`.\" if they're freelancers.)\n", - " - The domain is `domain`(or \"unspecified\" if no domain was provided).\n", - " - It's `words` words long, with a rate of `rate` € per word.\n", - " - It started on `st` and is due on `dl`, so `length` days are foreseen for it, of which `daysleft` left. To meet the deadline,`efficiency` words need to be translated or revised per day.\n", - " - There is a translation memory. (Or \"no\" if there's none.)\n", - " - The project is currently `status`.\"\n", - "\n", - "The script is documented with **docstrings** and has an **argparse parser**, so it can be run directly from the command line." - ] - }, - { - "cell_type": "markdown", - "id": "7b995db0", - "metadata": {}, - "source": [ - "### Expanded version\n", - "The expanded version of the script has all the features the basic version has, except for the argparse parser (due to time constraints). In addition, it has some extra features that the basic version does not have.\n", - "\n", - "Next to the `Project` class, the expanded tool has a `Freelancer` class to turn the entries of the freelancer database found in `freelancers_db.json` into objects. The **attributes** of that class are the same as the keys in that list of dictionaries. The class also has two **methods**, namely:\n", - "- A method to import arguments using keyword arguments (`kwargs`), rather than manually inputting each attribute;\n", - "- A `printing method`, which displays the freelancer information using the following text template:\n", - " - \"`name` can be contacted via e-mail (`email`) or via phone (`phone`).\n", - " - `name`'s reference in our database is `ref`.\n", - " - `name` works as a `task` for `language`.\"\n", - "\n", - "Due to time constraints, this class is **not documented with docstrings**.\n", - "\n", - "This added `Freelancer` class allows project managers to not only get an overview of their project's information, but to link the project database and the freelancer database to easily look up information about the freelancers assigned to a project (say, if they're late to deliver or something changes in the project organisation and the PM wants to contact them via e-mail)." - ] - }, - { - "cell_type": "markdown", - "id": "840d6e0d", - "metadata": {}, - "source": [ - "## How to use the project management tool" - ] - }, - { - "cell_type": "markdown", - "id": "d66823a8", - "metadata": {}, - "source": [ - "### Basic version\n", - "You can run the basic version of the script directly from the command line and manually feed it the project information, using:\n", - "\n", - "`python project-management-tool_basic.py \"title\" \"client\" \"source\" \"target\" words \"YYYY-MM-DD\" \"YYYY-MM-DD\" price tm`\n", - "\n", - "(The placeholders need to be replaced by the actual project information, check above that you input all the information as the right variable type, otherwise you will get an error message asking you to use the correct type.)" - ] - }, - { - "cell_type": "markdown", - "id": "cbe342bd", - "metadata": {}, - "source": [ - "### Expanded version\n", - "Because no argparse parser was programmed yet for this version of the tool, it needs to be opened rather than being run from the command line. You can open `project-management-tool_expanded.ipynb` with Jupyter Notebooks, or `project-management-tool_expanded.md` with, for example, VS Code. For more information about how to use these files, open them and go through the tutorial in them. Unlike with the basic version run from the command line, this version of the tool allows files as input to instantiate objects. It is possible to print the information of all the projects in one go, using `projects_db.json`, or to link the `Freelancer` and `Project` class for a single project, using `freelancers_db.json` and one of the separate project files." - ] - }, - { - "cell_type": "markdown", - "id": "804365fe", - "metadata": {}, - "source": [ - "## Future features\n", - "Due to time constraints, several features could not be implemented, which will be added in future updates:\n", - "- An argparse parser for the expanded version, which will enable the user to run it from the command line rather than having to open the script. Said parser will also work with files, rather than forcing the user to manually input every attribute, which will result in a significant time gain and make use of the tool easier.\n", - "- Validation and docstrings in the `Freelancer` class.\n", - "- Validation of the project deadline: ensure that the deadline comes *after* the start date, not before.\n", - "- Fine-tuning the `start` date and `deadline`, so the PM can either specify at what time of the day the project starts/needs to be delivered, or not specify it (in which case, the default start/end time is midnight for the start and 11:59 pm for the deadline).\n", - "- Differentiate between a rush assignment (`efficiency` calculated on 7/7 of the project lenth instead of 5/7) and a regular assignment (`efficiency` calculated on 5/7 of the project length).\n", - "- Fine-tuning of the `efficiency` by taking into account weekends and holidays (ex. so a regular four-days project is counted as four full work days if it spans from Monday to Thursday, but only two full work days if it spans from Friday to Monday, rather than calculating 5/7 of the full project length in both cases).\n", - "- A `Client` class with all the necessary client information (contact, VAT-number, bank account number, domain, unique reference inside the agency database...) This class will be linked to the `Project` class just like `Freelancer`, facilitating retrieval of client information.\n", - "- The possibility to loop through all the separate project files even when linking `Project` and `Freelancer` objects, so you don't have to run the script for each project separately (which is feasible for four projects, but far too time-consuming in a real agency setting).\n", - "- An extension of the recognised file formats, from json or csv to xml, excel-documents...\n", - "- Fine-tuning the `efficiency` attribute by creating a specific attribute for translation and for review (for example 2/3 of the time for translation and 1/3 for review).\n", - "- Fine-tuning the `rate` and `price` attributes by linking the rate to a `rate` entry in the freelancer database (since each freelancer has their own rates, and rate is not applicable to agency collaborators, who receive a fixed salary per month rather than being paid per project) and calculating the agency's margin after substraction of the freelancer fees from the total project price.\n", - "- Ensuring standardisation of the dates in a certain time zone, to avoid calculation errors if the start date and deadline were fixed in a different time zone from that of the person using the script (since their time zone determines the computation of \"today\")." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.9.16" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}