diff --git a/MyPortfolio/#data.py# b/MyPortfolio/#data.py# new file mode 100644 index 0000000000000000000000000000000000000000..5e1cc8254bbccb78e0114b97932591fe2f87e474 --- /dev/null +++ b/MyPortfolio/#data.py# @@ -0,0 +1,305 @@ +#!/.venv/bin/python + +# TODO + +# Gör project_id dynamisk så att den uppdateras efter borttagning/addering av projekt. + + + +# ---- IMPORTS ---- # +import os +import json +import pprint +import re +from operator import itemgetter +# ---- IMPORTS ---- # + + +def load(filename): + + try: + with open(filename, 'r', encoding='utf-8') as file: + data = json.load(file) + + file.close() + return data + except: + return None + + + +def save(data): + + with open('data.json', 'w', encoding='utf-8') as file: + json.dump(data, file, ensure_ascii=False, indent=4) + + file.close() + + + # Reload data + load() + + + +# Get project by ID +def get_project(data, id): + + for n in range(0, get_project_count(data)): + if data[n]['project_id'] == id: + return data[n] + + + +# Get project count +def get_project_count(data): + return len(data) + + + +# Get all unique techniques from project +def get_techniques(data): + + techniques = [] + for project in data: + for tech in project['techniques_used']: + if tech not in techniques: + techniques.append(tech) + + techniques.sort() + + return techniques + + + +# Gets all unique techniques from all projects ! COULD USE SOME FILTERING ! +def get_technique_stats(data): + + technique_list = get_techniques(data) + technique_stats = {} + current_techniques = [] + + for technique in technique_list: + for project in data: + if technique in project['techniques_used']: + current_techniques.append({'id' : project['project_id'], 'name' : project['project_name']}) + + technique_stats.update({technique : current_techniques.copy()}) + current_techniques.clear() + + + return technique_stats + + + +# Fetches and sorts projects matching criteria from the specified list. +def search(data, sort_by='project_id', sort_order='desc', techniques=None, search=None, search_field=None): + + results = [] + # get it + for project in data: + results.append(project) + + # sort it + results = sorted(results, key=itemgetter(sort_by)) + + # order it + if sort_order == 'asc': results.reverse() + + + # filter it (by techniques) + if techniques != None: + for technique in get_techniques(data): + for project in results: + + + if all(n in project['techniques_used'] for n in techniques): + pass + else: + results.pop(results.index(project)) + + # search for it + if search != None: + + search_results = [] + + for project in results: + + print(list(project.values())) + + if search_field != None and search_field != "": + if search in project[search_field]: + search_results.append(project) + + elif search_field == "": + results.clear() + + else: + for substring in list(project.values()): + print(type(substring)) + if type(substring) == str: + if substring.find(search) != -1: + search_results.append(project) + break + if type(substring) == int: + if str(substring).find(search) != -1: + search_results.append(project) + break + if type(substring) == list: + for subsubstring in substring: + if type(subsubstring) == str: + if subsubstring.find(search) != -1: + search_results.append(project) + break + if type(subsubstring) == int: + if str(subsubstring).find(search) != -1: + search_results.append(project) + break + + + + pprint.pp(results) + return results + + + +def cls(): + os.system('cls' if os.name == 'nt' else 'clear') + pass + + + +def new_project(data): + + cls() + + # ---- COLLECT INFO ---- + project_title = input("Project title: ") + project_id = get_project_count(data)+1 + techniques = input("\nWhat techniques does your project use? Write them out in the following format: python, java, html, css\n\nTechniques: ").replace(" ", "").lower().split(",") + description = input("Provide a description of your project: ") + url = input("Provide a link to the source code/demo of your project: ") + img_url = input("Image source (ex: logo.jpg): ") + # ---- COLLECT INFO ---- + + # lexicographical order sort aka alphabetical + techniques.sort() + + new_project = { + "project_name": project_title, + "project_id": project_id, + "used_techniques": techniques, + "long_description": description, + "img_url": img_url, + "url": url + } + + cls() + + print("\n\nProject preview:\n") + pprint.pp(new_project) + + option = int(input("\n1: Create\n2: Cancel\n> ")) + + if option == 1: + + data.append(new_project) + + save(data) + + pass + + + +def list_projects(data): + cls() + for project in data: + pprint.pp(project) + print("\n") + + + +def edit_project(data, id): + + while True: + if id > get_project_count(data) or id < 0: + print("Project ID doesn't exist.\n") + id = int(input("Project_ID to edit: ")) + else: + + cls() + + project = get_project(data, id) + project.pop('project_id') # Project ID shouldn't be changed + + print(f"Editing project: {project['title']}\n") + + pprint.pp(project) + print("") + + for field in enumerate(project): + print(f"{field[0]}: {field[1]}") + + input("\nField to edit: ") + + + + +def delete_project(data): + pass + + + +def menu(data): + + menu_items = ["Add new project", "List projects", "Edit existing project", "Delete project", "Quit"] + menu_index = 0 + + while True: + + cls() + + titular = r""" + ____ _ __ _ _ + | _ \ ___ _ __ | |_ / _| ___ | | (_) ___ + | |_) | / _ \ | '__| | __| | |_ / _ \ | | | | / _ \ + | __/ | (_) | | | | |_ | _| | (_) | | | _ | | | (_) | + |_| \___/ |_| \__| |_| \___/ |_| (_) |_| \___/ + """ + + print(titular) + + for i in menu_items: + print(f"{menu_items.index(i)+1}: {i}") + + try: + option = int(input(f"> ")) + + if option == 1: + new_project(data) + elif option == 2: + list_projects(data) + input() + elif option == 3: + list_projects(data) + edit_project(data, int(input("Project_ID to edit: "))) + elif option == 4: + delete_project(data, int(input("Project_ID to delete: "))) + elif option == 5: + cls() + break + except: + print("") + + + + + +def main(): + + data = load('MyPortfolio/data.json') + menu(data) + + search(data, 'project_id', 'desc', [], 'csv') + +if __name__ == "__main__": + main() diff --git a/MyPortfolio/#data_test.py# b/MyPortfolio/#data_test.py# new file mode 100644 index 0000000000000000000000000000000000000000..93705f3a570898faeec6023e1aff749aa4374d1f --- /dev/null +++ b/MyPortfolio/#data_test.py# @@ -0,0 +1,248 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (C) 2011, IDA, Linköping University +# Copyright (C) 2011, Torbjörn Lönnemark <tobbez@ryara.net> +# Copyright (C) 2014, Daniel Persson +import unittest +import data # import the file with your implemented functions +import hashlib +import sys +from operator import itemgetter + +# ----------- HELPER FUNCTIONS ----------- + +def print_tech_dict(d): + for k,v in d.items(): + print("{}: {}".format(k,v)) + for e in v: + print(e) + print() + +def sort_dict(d,sort_key): + for k in d.keys(): + d[k] = sorted(d[k], key = itemgetter(sort_key)) + return d; + +md5 = hashlib.md5 + + +# ----------- TEST CLASS ----------- + +class DataTest(unittest.TestCase): + """ Subclass of the unittest.TestCase class + + Define all tests as a method of this class. Each test must start with the + word test (ex test_load). Within each test method, various assertions + can be made, e.g. checking that what you are testing gives the expected + result. + + Use the method self.assertEqual() to compare an expected and observed result. + + Please refer to the unittest documentation for more information: + https://docs.python.org/3.7/library/unittest.html + + To run the tests, have the files data_test.py, data.py and data.json in the + same catalog. data.py is the file with your implemented API functions. + Execute with: + + $ python3 data_test.py + + The test result is shown in the terminal. + + """ + + def setUp(self): + """ The setUp() method is called before every test_*-method. Use it to + prepare things that must always be done before a test is run, such as + loading the data. + """ + + # The content in self.expected_data must match the content in data.json + # for the testing to work. Do NOT change the content or the file! + self.expected_data = [{'big_image': 'XXX', + 'project_name': 'python data-module test script', + 'course_name': 'OK\xc4NT', + 'group_size': 2, 'end_date': '2009-09-06', + 'techniques_used': ['python'], + 'academic_credits': 'WUT?', + 'small_image': 'X', + 'long_description': 'no no no', + 'course_id': 'TDP003', + 'project_id': 1, + 'external_link': 'YY', + 'short_description': 'no', + 'start_date': '2009-09-05', + 'lulz_had': 'many'}, + {'big_image': 'XXX', + 'project_name': 'NEJ', + 'course_name': 'OK\xc4NT', + 'group_size': 4, + 'end_date': '2009-09-08', + 'techniques_used': ['c++', 'csv', 'python'], + 'academic_credits': 'WUT?', + 'small_image': 'X', + 'long_description': 'no no no', + 'course_id': 'TDP003', + 'project_id': 3, + 'external_link': 'YY', + 'short_description': 'no', + 'start_date': '2009-09-07', + 'lulz_had': 'few'}, + {'big_image': 'XXX', + 'project_name': '2007', + 'course_name': 'OK\xc4NT', + 'group_size': 6, + 'end_date': '2009-09-09', + 'techniques_used': ['ada', 'python'], + 'academic_credits': 'WUT?', + 'small_image': 'X', + 'long_description': 'no no no', + 'course_id': 'TDP003', + 'project_id': 2, + 'external_link': 'YY', + 'short_description': 'no', + 'start_date': '2009-09-08', + 'lulz_had': 'medium'}, + {'big_image': 'XXX', + 'project_name': ',', + 'course_name': 'HOHO', + 'group_size': 8, + 'end_date': '2009-09-07', + 'techniques_used': [], + 'academic_credits': 'WUT?', + 'small_image': 'X', + 'long_description': 'no no no', + 'course_id': ' "', + 'project_id': 4, + 'external_link': 'YY', + 'short_description': 'no', + 'start_date': '2009-09-06', + 'lulz_had': 'over 9000'} + ] + + # Sort the expected data by project id + self.expected_data = sorted(self.expected_data, key=itemgetter('project_id')) + + # Store the hardcoded expected results. + # Do NOT change this part + self.expected_technique_data = ['ada', 'c++', 'csv', 'python'] + self.expected_technique_stat_data = {'python': [{'id': 2, 'name': '2007'}, + {'id': 3, 'name': 'NEJ'}, + {'id': 1, 'name': 'python data-module test script'}], + 'csv': [{'id': 3, 'name': 'NEJ'}], + 'c++': [{'id': 3, 'name': 'NEJ'}], + 'ada': [{'id': 2, 'name': '2007'}]} + + # Load the data using your implemented load function. The data is + # stored as a member of the class instance, so that it can be accessed + # in other methods of the class + self.loaded_data = sorted(data.load("data.json"), key=itemgetter('project_id')) + + def test_load(self): + """ Test the implemented load function """ + + # Compare the loaded data with the expected data + self.assertEqual(self.loaded_data[0], self.expected_data[0]) + + # Test that loading a non-existing file returns None + self.assertEqual(data.load("/dev/this_file_does_not_exist"), None) + + def test_get_project_count(self): + """ Test the implemented function get_project_count """ + + # Test that the correct number of projects are returned + self.assertEqual(data.get_project_count(self.loaded_data), 4) + + def test_get_project(self): + """ Test the implemented function get_project """ + + # Try to get project 1, 2, 3 and 4 and check that a project with + # the correct project_id is returned. + self.assertEqual(data.get_project(self.loaded_data, 1)['project_id'], 1) + self.assertEqual(data.get_project(self.loaded_data, 2)['project_id'], 2) + self.assertEqual(data.get_project(self.loaded_data, 3)['project_id'], 3) + self.assertEqual(data.get_project(self.loaded_data, 4)['project_id'], 4) + + # Try to get a non-existing project and check that None is returned + self.assertEqual(data.get_project(self.loaded_data, 42), None) + + def test_search(self): + """ Test the implemented search function """ + + # Call search with no other parameters than the database. + # All projects should be returned + self.assertEqual(len(data.search(self.loaded_data)), 4) + + # Search for projects with csv as technique. + # 1 project should be returned + self.assertEqual(len(data.search(self.loaded_data, techniques=['csv'])), 1) + + # Search for projects including Python and sort them in ascending order. + # Ensure that returned projects are sorted by ascending dates + res = data.search(self.loaded_data, sort_order='asc',techniques=["python"]) + self.assertEqual(res[0]['start_date'], '2009-09-05') + self.assertEqual(res[1]['start_date'], '2009-09-07') + self.assertEqual(res[2]['start_date'], '2009-09-08') + + # Search for the term 'okänt' in three specified search fields. Sort + # results by end_date. + # Ensure that projects are returned in the correct order. + res = data.search(self.loaded_data, + sort_by="end_date", + search='okänt', + search_fields=['project_id','project_name','course_name']) + self.assertEqual(len(res), 3) + self.assertEqual(res[0]['project_id'], 2) + self.assertEqual(res[1]['project_id'], 3) + self.assertEqual(res[2]['project_id'], 1) + + # Search for 'okänt' in specified search fields. + # Ensure correct number of results + res = data.search(self.loaded_data, + search="okänt", + search_fields=["project_id","project_name","course_name"]) + self.assertEqual(len(res), 3) + + # Search for 'okänt' in specified search fields, provide empty technique list + # Ensure correct number of results + res = data.search(self.loaded_data, + techniques=[], + search="okänt", + search_fields=["project_id","project_name","course_name"]) + self.assertEqual(len(res), 3) + + # Search for 'okänt', provide empty search fields list + # Ensure 0 results + res = data.search(self.loaded_data, search="okänt", search_fields=[]) + self.assertEqual(len(res), 0) + + # Search with results sorted by group size. + # Ensure results are in descending order + res = data.search(self.loaded_data, sort_by='group_size') + self.assertEqual(res[0]['project_id'], 4) #1 + self.assertEqual(res[1]['project_id'], 2) #2 + self.assertEqual(res[2]['project_id'], 3) #3 + self.assertEqual(res[3]['project_id'], 1) #4 + + def test_get_techniques(self): + """ Test the implemented get_techniques function """ + + res = data.get_techniques(self.loaded_data) + self.assertEqual(res, self.expected_technique_data) + + def test_get_technique_stats(self): + """ Test the implemented get_technique_stats function """ + + res = data.get_technique_stats(self.loaded_data) + res = sort_dict(res,'id') + + self.expected_technique_stat_data = sort_dict(self.expected_technique_stat_data,'id') + + self.assertEqual(res, self.expected_technique_stat_data) + + +if __name__ == '__main__': + print ("Test: ", md5(sys.argv[0].encode('UTF-8')).hexdigest()) + print ("Test data:", md5(b"data.json").hexdigest()) + print() + unittest.main() diff --git a/MyPortfolio/__pycache__/data.cpython-312.pyc b/MyPortfolio/__pycache__/data.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5bd0671c2f5cceecf65adcb36ceb10a5c03f89ca Binary files /dev/null and b/MyPortfolio/__pycache__/data.cpython-312.pyc differ diff --git a/MyPortfolio/__pycache__/data_layer.cpython-312.pyc b/MyPortfolio/__pycache__/data_layer.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79148307cb275dc61799188b73606a66dbd22e8c Binary files /dev/null and b/MyPortfolio/__pycache__/data_layer.cpython-312.pyc differ diff --git a/MyPortfolio/__pycache__/myFlaskProject.cpython-312.pyc b/MyPortfolio/__pycache__/myFlaskProject.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..041a3e262dbd27c08754b628b3f1b79be0b5923d Binary files /dev/null and b/MyPortfolio/__pycache__/myFlaskProject.cpython-312.pyc differ diff --git a/MyPortfolio/data_test.py b/MyPortfolio/data_test.py index 93705f3a570898faeec6023e1aff749aa4374d1f..363dcebb0f4a1bd3bfa950388bec2ac881863173 100644 --- a/MyPortfolio/data_test.py +++ b/MyPortfolio/data_test.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -# Copyright (C) 2011, IDA, Linköping University -# Copyright (C) 2011, Torbjörn Lönnemark <tobbez@ryara.net> +# Copyright (C) 2011, IDA, Linköping University +# Copyright (C) 2011, Torbjörn Lönnemark <tobbez@ryara.net> # Copyright (C) 2014, Daniel Persson import unittest import data # import the file with your implemented functions @@ -184,36 +184,36 @@ class DataTest(unittest.TestCase): self.assertEqual(res[1]['start_date'], '2009-09-07') self.assertEqual(res[2]['start_date'], '2009-09-08') - # Search for the term 'okänt' in three specified search fields. Sort + # Search for the term 'okänt' in three specified search fields. Sort # results by end_date. # Ensure that projects are returned in the correct order. res = data.search(self.loaded_data, sort_by="end_date", - search='okänt', + search='okänt', search_fields=['project_id','project_name','course_name']) self.assertEqual(len(res), 3) self.assertEqual(res[0]['project_id'], 2) self.assertEqual(res[1]['project_id'], 3) self.assertEqual(res[2]['project_id'], 1) - # Search for 'okänt' in specified search fields. + # Search for 'okänt' in specified search fields. # Ensure correct number of results res = data.search(self.loaded_data, - search="okänt", + search="okänt", search_fields=["project_id","project_name","course_name"]) self.assertEqual(len(res), 3) - # Search for 'okänt' in specified search fields, provide empty technique list + # Search for 'okänt' in specified search fields, provide empty technique list # Ensure correct number of results res = data.search(self.loaded_data, techniques=[], - search="okänt", + search="okänt", search_fields=["project_id","project_name","course_name"]) self.assertEqual(len(res), 3) - # Search for 'okänt', provide empty search fields list + # Search for 'okänt', provide empty search fields list # Ensure 0 results - res = data.search(self.loaded_data, search="okänt", search_fields=[]) + res = data.search(self.loaded_data, search="okänt", search_fields=[]) self.assertEqual(len(res), 0) # Search with results sorted by group size. diff --git a/MyPortfolio/static/images/chomp.webp b/MyPortfolio/static/images/chomp.webp new file mode 100644 index 0000000000000000000000000000000000000000..9b4de0c611f363514b3ce8b5e12a4fc44ae6b6dd Binary files /dev/null and b/MyPortfolio/static/images/chomp.webp differ diff --git a/MyPortfolio/static/images/logotest.png b/MyPortfolio/static/images/logotest.png new file mode 100644 index 0000000000000000000000000000000000000000..50d3aa8d9d73c0633c819454cb93ed1b710d8dd7 Binary files /dev/null and b/MyPortfolio/static/images/logotest.png differ diff --git a/installations-manual/installation-manual.pdf b/installations-manual/installation-manual.pdf deleted file mode 100644 index d29713d7203fb1f64ceeffe988dfad9e6ccbc2b9..0000000000000000000000000000000000000000 Binary files a/installations-manual/installation-manual.pdf and /dev/null differ diff --git a/installations-manual/installations_manual_utkast_olima957_taiku983.pdf b/installations-manual/installations_manual_utkast_olima957_taiku983.pdf deleted file mode 100644 index 824153d3c755392231e98d4d379d57955ca31c12..0000000000000000000000000000000000000000 Binary files a/installations-manual/installations_manual_utkast_olima957_taiku983.pdf and /dev/null differ