diff --git a/.idea/timefall.iml b/.idea/timefall.iml index 6dd7f5e..04c03b9 100644 --- a/.idea/timefall.iml +++ b/.idea/timefall.iml @@ -22,6 +22,7 @@ diff --git a/__pycache__/manage.cpython-38.pyc b/__pycache__/manage.cpython-38.pyc new file mode 100644 index 0000000..2ea454f Binary files /dev/null and b/__pycache__/manage.cpython-38.pyc differ diff --git a/db.sqlite3 b/db.sqlite3 index d1deb82..f92fe53 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/hr_module/__pycache__/admin.cpython-38.pyc b/hr_module/__pycache__/admin.cpython-38.pyc new file mode 100644 index 0000000..43bb924 Binary files /dev/null and b/hr_module/__pycache__/admin.cpython-38.pyc differ diff --git a/hr_module/__pycache__/apps.cpython-38.pyc b/hr_module/__pycache__/apps.cpython-38.pyc new file mode 100644 index 0000000..a00467e Binary files /dev/null and b/hr_module/__pycache__/apps.cpython-38.pyc differ diff --git a/hr_module/__pycache__/forms.cpython-38.pyc b/hr_module/__pycache__/forms.cpython-38.pyc new file mode 100644 index 0000000..03e027d Binary files /dev/null and b/hr_module/__pycache__/forms.cpython-38.pyc differ diff --git a/hr_module/__pycache__/models.cpython-38.pyc b/hr_module/__pycache__/models.cpython-38.pyc new file mode 100644 index 0000000..a4fbf57 Binary files /dev/null and b/hr_module/__pycache__/models.cpython-38.pyc differ diff --git a/hr_module/__pycache__/urls.cpython-38.pyc b/hr_module/__pycache__/urls.cpython-38.pyc index 8e583b3..dca41ab 100644 Binary files a/hr_module/__pycache__/urls.cpython-38.pyc and b/hr_module/__pycache__/urls.cpython-38.pyc differ diff --git a/hr_module/__pycache__/views.cpython-38.pyc b/hr_module/__pycache__/views.cpython-38.pyc index 192a0e9..8ef351b 100644 Binary files a/hr_module/__pycache__/views.cpython-38.pyc and b/hr_module/__pycache__/views.cpython-38.pyc differ diff --git a/hr_module/admin.py b/hr_module/admin.py index 8c38f3f..e7dde83 100644 --- a/hr_module/admin.py +++ b/hr_module/admin.py @@ -1,3 +1,9 @@ from django.contrib import admin # Register your models here. +from .models import Employee, TimeModel, Plan, TimeLog + +admin.site.register(Employee) +admin.site.register(TimeModel) +admin.site.register(Plan) +admin.site.register(TimeLog) diff --git a/hr_module/forms.py b/hr_module/forms.py index e69de29..bd15122 100644 --- a/hr_module/forms.py +++ b/hr_module/forms.py @@ -0,0 +1,19 @@ +from django import forms +from django.conf import settings + +class UploadFileForm(forms.Form): + file = forms.FileField(widget=forms.ClearableFileInput(attrs={'id': 'import_filepicker'})) + + +class NewUserForm(forms.Form): + first_name = forms.CharField(max_length=200) + last_name = forms.CharField(max_length=200) + username = forms.CharField(max_length=200) + email = forms.EmailField(max_length=200) + is_staff = forms.BooleanField() + is_active = forms.BooleanField() + is_superuser = forms.BooleanField() + department = forms.CharField(max_length=200) + manager_username = forms.CharField(max_length=200) + time_model_id = forms.IntegerField() + manager_flag = forms.BooleanField() \ No newline at end of file diff --git a/hr_module/handling_functions/__pycache__/__init__.cpython-38.pyc b/hr_module/handling_functions/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..d4e91f0 Binary files /dev/null and b/hr_module/handling_functions/__pycache__/__init__.cpython-38.pyc differ diff --git a/hr_module/handling_functions/__pycache__/data_import_functions.cpython-38.pyc b/hr_module/handling_functions/__pycache__/data_import_functions.cpython-38.pyc new file mode 100644 index 0000000..a2f44a4 Binary files /dev/null and b/hr_module/handling_functions/__pycache__/data_import_functions.cpython-38.pyc differ diff --git a/hr_module/handling_functions/__pycache__/monthly_planning_functions.cpython-38.pyc b/hr_module/handling_functions/__pycache__/monthly_planning_functions.cpython-38.pyc new file mode 100644 index 0000000..02aa54e Binary files /dev/null and b/hr_module/handling_functions/__pycache__/monthly_planning_functions.cpython-38.pyc differ diff --git a/hr_module/handling_functions/__pycache__/sqlalchemy_tables.cpython-38.pyc b/hr_module/handling_functions/__pycache__/sqlalchemy_tables.cpython-38.pyc new file mode 100644 index 0000000..40fd9ca Binary files /dev/null and b/hr_module/handling_functions/__pycache__/sqlalchemy_tables.cpython-38.pyc differ diff --git a/hr_module/handling_functions/data_import_functions.py b/hr_module/handling_functions/data_import_functions.py index 5a9a3d1..c25aa08 100644 --- a/hr_module/handling_functions/data_import_functions.py +++ b/hr_module/handling_functions/data_import_functions.py @@ -1,9 +1,8 @@ import pandas as pd -import sqlite3 from django.conf import settings import datetime - +from sqlalchemy import create_engine def create_connection(db_file): """ create a database connection to the SQLite database @@ -13,8 +12,10 @@ def create_connection(db_file): """ conn = None try: - conn = sqlite3.connect(db_file) - except Error as e: + path = 'sqlite:///' + str(db_file) + engine = create_engine(path) + conn = engine.connect() + except Exception as e: print(e) return conn @@ -28,36 +29,8 @@ def read_and_parse_excel(file): return {'df_html': html, 'df_path': path_temp} -def load_existing_users(): - conn = create_connection(settings.DATABASES['default']['NAME']) - cur = conn.cursor() - cur.execute('select username from auth_user') - usernames = [user[0] for user in cur.fetchall()] - return usernames - - -def generate_username(first_name, last_name, usernames): - username = first_name + last_name[:1] - i = 1 - while username + str(i) in usernames: - i += 1 - - username = username + str(i) - return username.lower() - - def insert_excel(df): conn = create_connection(settings.DATABASES['default']['NAME']) - - usernames = load_existing_users() - print(usernames) - for i in range(len(df)): - row = df.iloc[i] - username = generate_username(row['first_name'], row['last_name'], usernames) - df.at[i, 'username'] = username - usernames.append(username) - - df['date_joined'] = datetime.date.today() df['password'] = 'start' diff --git a/hr_module/handling_functions/monthly_planning_functions.py b/hr_module/handling_functions/monthly_planning_functions.py index e69de29..7f29482 100644 --- a/hr_module/handling_functions/monthly_planning_functions.py +++ b/hr_module/handling_functions/monthly_planning_functions.py @@ -0,0 +1,46 @@ + +from sqlalchemy.orm import sessionmaker +from sqlalchemy import create_engine +from django.conf import settings +from ..models import Employee, TimeModel, Plan +import datetime + + +def time_timedelta(time, timedelta): + start = datetime.datetime(2020, 1, 1, time.hour, time.minute) + end = start + timedelta + return end.time() + +def main(user, start_date, end_date, start_time, activity_type, timemodel): + + start_date = datetime.datetime.strptime(start_date, '%Y-%m-%d') + end_date = datetime.datetime.strptime(end_date, '%Y-%m-%d') + start_time = datetime.datetime.strptime(start_time, '%H:%M') + + username = Employee.objects.get(username=user) + + daily_hours = int(timemodel.daily_hours * 60) + time_end = time_timedelta(start_time, datetime.timedelta(minutes=daily_hours)) + + timemodel_pattern = {0: timemodel['mon'], + 1: timemodel['tue'], + 2: timemodel['wed'], + 3: timemodel['thu'], + 4: timemodel['fri'], + 5: timemodel['sat'], + 6: timemodel['sun']} + + days = (end_date - start_date).days + for day in range(days): + date_to_add = start_date + datetime.timedelta(days=day) + if timemodel_pattern[date_to_add.weekday()]: + print(start_time, time_end) + plan = Plan(username=username, + date=date_to_add, + begin_time=start_time, + end_time=time_end, + activity_type=activity_type) + plan.save() + + + diff --git a/hr_module/handling_functions/sqlalchemy_tables.py b/hr_module/handling_functions/sqlalchemy_tables.py index e69de29..843efa4 100644 --- a/hr_module/handling_functions/sqlalchemy_tables.py +++ b/hr_module/handling_functions/sqlalchemy_tables.py @@ -0,0 +1,47 @@ +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import Column, Integer, String, Boolean, Date, Numeric, Time + + +Base = declarative_base() + +class Employee(Base): + __tablename__ = 'hr_module_employee' + + username = Column(String, primary_key=True) + department = Column(String) + manager_username = Column(String) + time_model_id = Column(Integer) + manager_flag = Column(Boolean) + +class TimeModel(Base): + __tablename__ = 'hr_module_timemodel' + + time_model_id = Column(Integer, primary_key=True) + daily_hours = Column(Numeric) + weekly_days = Column(Integer) + mon = Column(Boolean) + tue = Column(Boolean) + wed = Column(Boolean) + thu = Column(Boolean) + fri = Column(Boolean) + sat = Column(Boolean) + sun = Column(Boolean) + +class Plan(Base): + __tablename__ = 'hr_module_plan' + username = Column(String, primary_key=True) + date = Column(Date) + begin_time = Column(Time) + end_time = Column(Time) + activity_type = Column(String) + +class TimeLog(Base): + __tablename__ = 'hr_module_timelog' + username = Column(String, primary_key=True) + date = Column(Date) + begin_time = Column(Time) + end_time = Column(Time) + activity_type = Column(String) + + + diff --git a/hr_module/models.py b/hr_module/models.py index dc15f5b..6cbf201 100644 --- a/hr_module/models.py +++ b/hr_module/models.py @@ -1,17 +1,50 @@ from django.db import models +from django.contrib.auth.models import User + +ACTIVITY_TYPES = [('work', 'Work'), + ('vacation', 'Vacation'), + ('sickness', 'Sickness'), + ('parental_leave', 'Parental leave'), + ] + # Create your models here. class Employee(models.Model): - person_id = models.IntegerField(blank=False, - null=False, - unique=True) - first_name = models.CharField(max_length=200, - null=False, - blank=False) - last_name = models.CharField(max_length=200, - null=False, - blank=False) + username = models.OneToOneField(User, on_delete=models.CASCADE, + to_field='username', + db_column="username", + primary_key=True) department = models.CharField(max_length=200, null=False, blank=False) - manager_id = models.IntegerField() \ No newline at end of file + manager_username = models.CharField(max_length=200) + time_model = models.ForeignKey('TimeModel', on_delete=models.PROTECT) + manager_flag = models.BooleanField() + + +class TimeModel(models.Model): + time_model_id = models.IntegerField(primary_key=True) + daily_hours = models.DecimalField(max_digits=4, decimal_places=2) + weekly_days = models.IntegerField() + mon = models.BooleanField() + tue = models.BooleanField() + wed = models.BooleanField() + thu = models.BooleanField() + fri = models.BooleanField() + sat = models.BooleanField() + sun = models.BooleanField() + +class Plan(models.Model): + username = models.ForeignKey(Employee, on_delete=models.PROTECT) + date = models.DateField() + begin_time = models.TimeField() + end_time = models.TimeField() + activity_type = models.CharField(max_length=100) + + +class TimeLog(models.Model): + username = models.ForeignKey(Employee, on_delete=models.PROTECT) + date = models.DateField() + begin_time = models.TimeField() + end_time = models.TimeField() + activity_type = models.CharField(max_length=100) diff --git a/hr_module/static/css/hr_import_validation.css b/hr_module/static/css/hr_import_validation.css index e69de29..821f485 100644 --- a/hr_module/static/css/hr_import_validation.css +++ b/hr_module/static/css/hr_import_validation.css @@ -0,0 +1,23 @@ + + +.import_table { + float:left; + overflow: auto; +} + +table, th, td { + border: 1px solid lightgray; + padding: 5px; + width: auto; + text-align: center; + border-collapse: collapse; + border-width: thin; +} + +table tr:nth-child(even){ + background-color: #f2f2f2; +} + +tr:hover { + +} \ No newline at end of file diff --git a/hr_module/static/css/hr_module_base.css b/hr_module/static/css/hr_module_base.css index e69de29..a0c144f 100644 --- a/hr_module/static/css/hr_module_base.css +++ b/hr_module/static/css/hr_module_base.css @@ -0,0 +1,30 @@ + +.hr_module_main_container { + padding: 1%; + float: left; + width: 80%; + height: 100%; + background-color: whitesmoke; +} +.sidebar { + background-color: #8b0000; + width: 18%; + height: 100%; + float: left; +} + +.sidebar_item { + background-color: navajowhite; + margin-right: 6px; + margin-left: 6px; + margin-top: 6px; + padding: 10px; + font-size: 20px; + +} + +.sidebar_item:hover { + background-color: white; + cursor: pointer; +} + diff --git a/hr_module/static/css/hr_module_import.css b/hr_module/static/css/hr_module_import.css index e69de29..89a94db 100644 --- a/hr_module/static/css/hr_module_import.css +++ b/hr_module/static/css/hr_module_import.css @@ -0,0 +1,95 @@ + + +label { + width: 40%; + display:inline-block; +} + +table { + margin-top: 10px; +} + +table, th, td { + border: 1px solid lightgray; + padding: 5px; + width: auto; + text-align: center; + border-collapse: collapse; + border-width: thin; +} + +.import_container { + float: left; + width: 40%; + height: 70%; +} + + + +.form_element, .import_text { + height: 30px; +} + +.separator { + position: relative; + float: left; + width: 10%; + height: 70%; +} + +.separatorline { + position: absolute; + left: 49%; + top: 0; + bottom: 0; + width: 1px; + background: black; + z-index: 1; +} + +.wordwrapper { + text-align: center; + height: 12px; + position: absolute; + left: 0; + right: 0; + top: 50%; + margin-top: -12px; + z-index: 2; +} + +.word { + color: #cccccc; + text-transform: uppercase; + letter-spacing: 1px; + padding: 3px; + font: bold 12px; + background-color: whitesmoke; +} + +.import_execute { + padding: 5px; + width: 30%; + text-align: center; + margin: 10px; +} + +#import_filepicker { + padding: 5px; + width: 90%; + text-align: center; + margin: 10px; +} + + + + + + + + + + + + + diff --git a/hr_module/static/css/hr_module_schedule.css b/hr_module/static/css/hr_module_schedule.css index e69de29..e3cecf1 100644 --- a/hr_module/static/css/hr_module_schedule.css +++ b/hr_module/static/css/hr_module_schedule.css @@ -0,0 +1,38 @@ + +.scheduleSearchRow { + height: 35px; + +} + +.search_element { + float: left; + width: 20%; +} + +#schedule_filter_category { + margin-right: 10px; +} + +.day_of_week_checkbox { + float: left; + height: 36px; + padding: 4px; +} + +input, label{ + display: block; +} + +table, th, td { + border: 1px solid lightgray; + padding: 5px; + width: auto; + text-align: center; + border-collapse: collapse; + border-width: thin; +} + +.date_select_row { + margin-top: 10px; + margin-bottom: 10px; +} \ No newline at end of file diff --git a/hr_module/static/js/hr_module_schedule.js b/hr_module/static/js/hr_module_schedule.js index e69de29..7efff7f 100644 --- a/hr_module/static/js/hr_module_schedule.js +++ b/hr_module/static/js/hr_module_schedule.js @@ -0,0 +1,244 @@ +const csrftoken = getCookie('csrftoken'); + +var searchInput = document.getElementById('searched_string'); +searchInput.addEventListener("click", fetchSearchOptions); + +var fetchEmployeesButton = document.getElementById('load_employees'); +fetchEmployeesButton.addEventListener('click', fetchEmployees); + +var submitToDb = document.getElementById('run_planning'); +submitToDb.addEventListener('click', postPlanToDb); + +function fetchSearchOptions() { + var host = 'http://' + window.location.host; + var fetch_url = host + '/hr_module/search' + + var searched_field = document.getElementById('schedule_filter_category').value + var searched_string = document.getElementById('searched_string').value + + fetch(fetch_url, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': csrftoken + }, + body: JSON.stringify({ + "searched_field": searched_field, + "searched_string": searched_string + }), + }).then((response) => { + return response.json(); + }).then((data) => { + console.log(data); + appendFoundFilterOptions(data); + }); +} + + +function appendFoundFilterOptions(data){ + console.log(data) + var parent = document.getElementById('schedule_filter_search'); + parent.innerHTML = ''; + for (var i = 0; i < data.length; i++) { + + var caption = data[i]['caption']; + var value = data[i]['value']; + console.log(caption) + console.log(value) + + innerHTML = '' + + var wrapper = document.createElement('div'); + wrapper.innerHTML = innerHTML; + var htmlNode = wrapper.firstChild; + + parent.appendChild(htmlNode); + } +} + +function fetchEmployees() { + var host = 'http://' + window.location.host; + var fetch_url = host + '/hr_module/loeademployees' + + var searched_field = document.getElementById('schedule_filter_category').value + var searched_string = document.getElementById('searched_string').value + + fetch(fetch_url, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': csrftoken + }, + body: JSON.stringify({ + "searched_field": searched_field, + "searched_string": searched_string + }), + }).then((response) => { + return response.json(); + }).then((data) => { + console.log(data); + appendFoundEmployees(data); + }); +} + +function appendFoundEmployees(data){ + console.log(data) + var parentTable = document.getElementById('scheduling_list'); + parent.innerHTML = ''; + for (var i = 0; i < data.length; i++) { + + var name = data[i]['name']; + var username = data[i]['username']; + var manager_name = data[i]['manager_name']; + var daily_hours = data[i]['daily_hours']; + + + var newRow = parentTable.insertRow(-1) + newRow.className = 'employee_row' + newRow.id = 'employee_row_' + username + newRow.setAttribute('data-username', username) + addCell(newRow, username, 0) + addCell(newRow, name, 1) + addCell(newRow, manager_name, 2) + addCell(newRow, daily_hours, 3) + var cellDays = addCell(newRow, '', 4) + workingDays(cellDays, data[i]) + + addCell(newRow, addActivityType(), 5) + + addCell(newRow,'', 6) + var cellButton = addCell(newRow, addDeleteButton(username),7) + cellButton.firstChild.addEventListener('click', function(){ + deleteRow(username) + }) + } +} + +function addDeleteButton(username){ + var cell = document.createElement('td') + var submit = document.createElement('button') + submit.innerHTML = 'Delete' + cell.appendChild(submit) + return cell.outerHTML +} + +function addActivityType(){ + var cell = document.createElement('td') + var options = {'work': 'Work', + 'vacation': 'Vacation', + 'sickness': 'Sickness', + 'parental_leave': 'Parental leave'} + var select = document.createElement('select') + select.id = 'activity_type' + + for (const [key, value] of Object.entries(options)) { + var option = document.createElement('option'); + option.value = key; + option.text = value; + select.appendChild(option) + } + + cell.appendChild(select) + return cell.outerHTML +} + +function addCell(row, innerhtml, index){ + var cell = row.insertCell(index); + cell.innerHTML = innerhtml; + return cell +} + +function workingDays(parent, data){ + var days = ['mon', + 'tue', + 'wed', + 'thu', + 'fri', + 'sat', + 'sun',] + days.forEach(function (item) { + if (data[item] === true) { + var tag = '' + + } else { + var tag = '' + } + var wrapper = document.createElement('div'); + wrapper.className = 'day_of_week_checkbox' + wrapper.innerHTML = tag; + parent.appendChild(wrapper) + }); +} + +function deleteRow(username){ + var row = document.getElementById('employee_row_' + username) + row.outerHTML = '' +} + +function buildPlanJson(){ + var listJsons = [] + var employees = document.getElementsByClassName('employee_row') + var lowerBoundary = document.getElementById('planning_lower_boundary').value + var upperBoundary = document.getElementById('planning_upper_boundary').value + for (var i = 0; i < employees.length; i++) { + var planJson = {} + row = employees[0] + var username = row.getAttribute('data-username') + var startTime = row.getElementsByClassName('schedule_time_input')[0].value + var days = row.getElementsByClassName('working_days') + var daysOfWeek = {} + for (var k = 0; k < days.length; k++){ + day = days[k] + daysOfWeek[day.value] = day.checked + } + planJson['username'] = username + planJson['start_time'] = startTime + planJson['timemodel_pattern'] = daysOfWeek + planJson['date_start'] = lowerBoundary + planJson['date_end'] = upperBoundary + listJsons.push(planJson) + } + return listJsons +} + +function postPlanToDb(){ + var host = 'http://' + window.location.host; + var fetch_url = host + '/hr_module/plan_api' + var json = buildPlanJson() + + fetch(fetch_url, + { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-CSRFToken': csrftoken + }, + body: JSON.stringify({ + json + }), + }).then((response) => { + return true; + }).then((data) => { + console.log(data); + }); +} + + + +function getCookie(name) { + let cookieValue = null; + if (document.cookie && document.cookie !== '') { + let cookies = document.cookie.split(';'); + for (let i = 0; i < cookies.length; i++) { + let cookie = cookies[i].trim(); + + if (cookie.substring(0, name.length + 1) === (name + '=')) { + cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); + break; + } + } + } + return cookieValue; +} \ No newline at end of file diff --git a/hr_module/templates/hr_import_validation.html b/hr_module/templates/hr_import_validation.html index 4bdcfcb..2117b41 100644 --- a/hr_module/templates/hr_import_validation.html +++ b/hr_module/templates/hr_import_validation.html @@ -1,10 +1,17 @@ - - - - - $Title$ - - -$END$ - - \ No newline at end of file +{% extends 'hr_module_base.html' %} + +{% load static %} + +{% block action_panel %} + +
The following data will be imported: +
+
+ {{ df_html|safe }} +
+
+ {% csrf_token %} + +
+ +{% endblock %} diff --git a/hr_module/templates/hr_module_base.html b/hr_module/templates/hr_module_base.html index 4bdcfcb..24716d6 100644 --- a/hr_module/templates/hr_module_base.html +++ b/hr_module/templates/hr_module_base.html @@ -1,10 +1,32 @@ - - - - - $Title$ - - -$END$ - - \ No newline at end of file +{% extends 'base.html' %} +{% load static %} + +{% block stylesheet_hr_module_base %} + +{% endblock %} + + + +{% block core_content %} + + + +
+{% block action_panel %} +{% endblock %} +
+ +{% endblock %} \ No newline at end of file diff --git a/hr_module/templates/hr_module_import.html b/hr_module/templates/hr_module_import.html index 4bdcfcb..7ffb83c 100644 --- a/hr_module/templates/hr_module_import.html +++ b/hr_module/templates/hr_module_import.html @@ -1,10 +1,94 @@ - - - - - $Title$ - - -$END$ - - \ No newline at end of file +{% extends 'hr_module_base.html' %} + +{% load static %} + +{% block action_panel %} + +
+
+ Add employee manually +
+
+ +
+ + {{ userform.first_name }} +
+
+ + {{ userform.last_name }} +
+
+ + {{ userform.username }} +
+
+ + {{ userform.email }} +
+
+ + {{ userform.is_staff }} +
+
+ + {{ userform.is_active }} +
+
+ + {{ userform.is_superuser }} +
+
+ + {{ userform.manager_flag }} +
+
+ + {{ userform.department }} +
+
+ + {{ userform.manager_username }} +
+
+ + {{ userform.time_model_id }} +
+ {% csrf_token %} + +
+
+ +
+
+
+
or
+
+
+ +
+
+ Import employees from Excel file +
+ + + + + + {% for item in columns %} + + + + + {% endfor %} +
Column nameData type
{{ item }}
+
+ {% csrf_token %} +
+ {{ fileform.file }} +
+ +
+
+ +{% endblock %} diff --git a/hr_module/templates/hr_module_schedule.html b/hr_module/templates/hr_module_schedule.html index 4bdcfcb..695dd2b 100644 --- a/hr_module/templates/hr_module_schedule.html +++ b/hr_module/templates/hr_module_schedule.html @@ -1,10 +1,57 @@ - - - - - $Title$ - - -$END$ - - \ No newline at end of file +{% extends 'hr_module_base.html' %} + +{% load static %} + +{% block action_panel %} + + +
+
Select filter type: + +
+ +
+ + + +
+
+ +
+
+ + + + + + + + + + + + + +
UsernameNameName of the ManagerDaily working hoursDefault scheduleActivity typeStart time
+ +
+
+ +
+ +
+ +
+ +
+ +
+
+ + + +{% endblock %} diff --git a/hr_module/urls.py b/hr_module/urls.py index 91f65b2..c3c69c3 100644 --- a/hr_module/urls.py +++ b/hr_module/urls.py @@ -2,6 +2,13 @@ from django.urls import path from . import views +app_name = 'hr_module' + urlpatterns = [ path('', views.index, name='index'), + path('import_data', views.hr_import, name='import_data'), + path('search', views.search_users_api, name='search_users_api'), + path('loeademployees', views.load_employees_api, name='load_employees'), + path('plan_api', views.plan_api, name='plan_api'), + ] \ No newline at end of file diff --git a/hr_module/views.py b/hr_module/views.py index a189121..38f8eac 100644 --- a/hr_module/views.py +++ b/hr_module/views.py @@ -1,6 +1,147 @@ -from django.shortcuts import render -from django.http import HttpResponse +from django.shortcuts import render, redirect +from .forms import UploadFileForm, NewUserForm +from hr_module.handling_functions.data_import_functions import read_and_parse_excel, insert_excel +import pandas as pd +import json +from .models import Employee +from django.core import serializers +from django.http import HttpResponse, JsonResponse, Http404 +from django.db import connection # Create your views here. def index(request): - return HttpResponse("Hello, world. You're at the app index.") \ No newline at end of file + template_name = 'hr_module_schedule.html' + + return render(request, template_name) + + +def hr_import(request): + if request.method == 'POST': + if 'import_preview' in request.POST: + fileform = UploadFileForm(request.POST, request.FILES) + print(fileform.is_valid()) + if fileform.is_valid(): + uploaded_file = request.FILES['file'] + df_dict = read_and_parse_excel(uploaded_file) + + request.session['df_path'] = df_dict['df_path'] + df_html = df_dict['df_html'] + context = {'df_html': df_html} + template = 'hr_import_validation.html' + return render(request, template, context) + + if 'import_insert' in request.POST: + df = pd.read_csv(request.session['df_path']) + insert_excel(df) + del request.session['df_path'] + return redirect('import_data') + + + if 'import_single' in request.POST: + df_dict = {k: v[0] for k, v in dict(request.POST).items()} + for i in ('csrfmiddlewaretoken','import_single'): + df_dict.pop(i, None) + df = pd.DataFrame(df_dict, index=[0]) + insert_excel(df) + print('done') + return redirect('import_data') + + else: + columns_user = ['first_name', + 'last_name', + 'username', + 'email', + 'is_staff', + 'is_active', + 'is_superuser'] + columns_empl = ['department', + 'manager_username', + 'time_model_id', + 'manager_flag'] + columns = columns_user + columns_empl + + fileform = UploadFileForm() + userform = NewUserForm() + context = {'userform': userform, + 'columns': columns, + 'fileform': fileform + } + template = 'hr_module_import.html' + return render(request, template, context) + + +def dictfetchall(cursor): + "Return all rows from a cursor as a dict" + columns = [col[0] for col in cursor.description] + return [ + dict(zip(columns, row)) + for row in cursor.fetchall() + ] + +def search_users_api(request): + if request.method == 'POST': + body = json.loads(request.body) + searched_field = body['searched_field'] + searched_string = body['searched_string'] + print(searched_field, searched_string) + cursor = connection.cursor() + + + if searched_field == 'department': + cursor.execute('select department as caption, department as value from hr_module_employee ' + 'group by department') + elif searched_field == 'manager': + cursor.execute('select auth.first_name || %s || auth.last_name as caption, empl.manager_username as value ' + 'from hr_module_employee empl inner join auth_user auth on auth.username = empl.manager_username ' + 'group by auth.first_name || %s || auth.last_name, empl.manager_username', [' ', ' '] + ) + elif searched_field == 'username': + cursor.execute('select auth.first_name || %s || auth.last_name as caption, empl.username as value from hr_module_employee empl ' + 'inner join auth_user auth on empl.username = auth.username ', [' ',] + ) + + response = dictfetchall(cursor) + print(response) + return JsonResponse(response, safe=False) + + +def load_employees_api(request): + if request.method == 'POST': + body = json.loads(request.body) + searched_field = body['searched_field'] + searched_string = body['searched_string'] + print(searched_field, searched_string) + cursor = connection.cursor() + + + if searched_field == 'department': + cursor.execute('select auth.first_name || %s || auth.last_name as name, auth.username as username, ' + 'empl.manager_username as manager_name, tm.daily_hours, ' + 'tm.mon, tm.tue, tm.wed, tm.thu, tm.fri, tm.sat, tm.fri ' + 'from hr_module_employee empl inner join auth_user auth on auth.username = empl.manager_username ' + 'inner join hr_module_timemodel tm on empl.time_model_id = tm.time_model_id ' + 'where department = %s', [' ', searched_string]) + elif searched_field == 'manager': + cursor.execute('select auth.first_name || %s || auth.last_name as name, auth.username as username, ' + 'empl.manager_username as manager_name, tm.daily_hours, ' + 'tm.mon, tm.tue, tm.wed, tm.thu, tm.fri, tm.sat, tm.fri ' + 'from hr_module_employee empl inner join auth_user auth on auth.username = empl.manager_username ' + 'inner join hr_module_timemodel tm on empl.time_model_id = tm.time_model_id ' + 'where manager_username = %s', [' ', searched_string]) + + elif searched_field == 'username': + cursor.execute('select auth.first_name || %s || auth.last_name as name, auth.username as username, ' + 'empl.manager_username as manager_name, tm.daily_hours, ' + 'tm.mon, tm.tue, tm.wed, tm.thu, tm.fri, tm.sat, tm.sun ' + 'from hr_module_employee empl inner join auth_user auth on auth.username = empl.manager_username ' + 'inner join hr_module_timemodel tm on empl.time_model_id = tm.time_model_id ' + 'where auth.username = %s ', [' ', searched_string]) + + response = dictfetchall(cursor) + print(response) + return JsonResponse(response, safe=False) + + +def plan_api(request): + + print(json.loads(request.body)) \ No newline at end of file diff --git a/static/css/base.css b/static/css/base.css index b248c23..14408a9 100644 --- a/static/css/base.css +++ b/static/css/base.css @@ -1,8 +1,16 @@ - -.top-bar { - height: 150; - background: #C0C0C0; - margin-left: auto; - margin-right: auto; +html, body { + width: 100%; + height: 100%; + font-family: sans-serif; +} +.navbar { + height: 50px; + background-color: #D3D3D3; + width: 100%; +} +.main_container { + background-color: whitesmoke; + width: 100%; + height: 100%; } \ No newline at end of file diff --git a/temp/import_file.csv b/temp/import_file.csv index eebff6f..f85f523 100644 --- a/temp/import_file.csv +++ b/temp/import_file.csv @@ -1,23 +1,6 @@ -,first_name,last_name,email,is_staff,is_active,is_superuser,department,manager_username,time_model_id,manager_flag -0,test,test2,testtest2@fff.com,False,True,False,test department,93099,10,0 -1,test,sf,testsf@fff.com,False,True,False,test department,123555,5,0 -2,test,fsdc,testfsdc@fff.com,False,True,False,dfdfdv,58347,14,0 -3,test,sdvcds,testsdvcds@fff.com,False,True,False,fsdfgfg,21411,11,0 -4,test,vsd,testvsd@fff.com,False,True,False,fg,100670,14,0 -5,gf,ddsf,gfddsf@fff.com,False,True,False,fsdfgfg,56776,1,1 -6,fg,bg,fgbg@fff.com,False,True,False,fg,1012,7,1 -7,gf,gb,gfgb@fff.com,False,True,False,fsdfgfg,24615,6,1 -8,fg,gb,fggb@fff.com,False,True,False,dfgdfgffgs,131029,10,1 -9,fg,gf,fggf@fff.com,False,True,False,fsdfgfg,150186,6,1 -10,gf,bfg,gfbfg@fff.com,False,True,False,sfgdfsgdfg,127720,7,0 -11,dfg,bvdf,dfgbvdf@fff.com,False,True,False,test department,21584,13,1 -12,fg,test2,fgtest2@fff.com,False,True,False,test department,106721,6,0 -13,first_name,bfg,first_namebfg@fff.com,False,True,False,test department,151398,10,0 -14,dfg,test2,dfgtest2@fff.com,False,True,False,test department,18196,1,0 -15,gf,gbb,gfgbb@fff.com,False,True,False,fsdfgfg,23266,11,0 -16,dfg,fd,dfgfd@fff.com,False,True,False,fsdfgfg,109517,15,0 -17,gf,bsdgbdf,gfbsdgbdf@fff.com,False,True,False,fsdfgfg,56302,13,1 -18,gf,fd,gffd@fff.com,False,True,False,fsdfgfg,149169,11,1 -19,dfg,gdf,dfggdf@fff.com,False,True,False,fsdfgfg,83458,3,0 -20,dfg,gb,dfggb@fff.com,False,True,False,fsdfgfg,12670,6,0 -21,test,test2,testtest2@fff.com,False,True,False,test department,49135,3,1 +,first_name,last_name,username,email,is_staff,is_active,is_superuser,department,manager_username,time_model_id,manager_flag +0,Jan,Kowalski,kowalskija,kowalskija@company.com,0,1,0,test department,hitlerad,1,0 +1,Janina,Kowalska,kowalskaja,kowalskaja@company.com,0,1,0,test department,hitlerad,2,0 +2,Tadeusz,Tadeuszowicz,tadeuszowiczta,tadeuszowiczta@company.com,0,1,0,dfdfdv,hitlerad,3,0 +3,Aleksander,Lukaszenko,lukaszenkoal,lukaszenkoal@company.com,0,1,0,fsdfgfg,hitlerad,4,0 +4,Adolf,Hitler,hitlerad,hitlerad@company.com,0,1,0,fg,hitlerad,5,0 diff --git a/templates/base.html b/templates/base.html index 4bdcfcb..8976638 100644 --- a/templates/base.html +++ b/templates/base.html @@ -1,10 +1,24 @@ +{% load static %} + - - $Title$ + + Title + + {% block stylesheet_hr_module_base %} + {% endblock %} -$END$ + + +
+ + {% block core_content %} + {% endblock %} + +
+ \ No newline at end of file diff --git a/timefall/__pycache__/settings.cpython-38.pyc b/timefall/__pycache__/settings.cpython-38.pyc index 9ce795e..f2bc508 100644 Binary files a/timefall/__pycache__/settings.cpython-38.pyc and b/timefall/__pycache__/settings.cpython-38.pyc differ diff --git a/timefall/settings.py b/timefall/settings.py index 9dec448..225e1d9 100644 --- a/timefall/settings.py +++ b/timefall/settings.py @@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/3.1/ref/settings/ """ from pathlib import Path +import os # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -25,12 +26,14 @@ SECRET_KEY = 'tx#+u3u-r1$n_4r(!6vvcb@1f5!z21^74w(zesiz$&59&72$kq' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = [] +ALLOWED_HOSTS = ['192.168.101.128', + '127.0.0.1',] # Application definition INSTALLED_APPS = [ + 'hr_module.apps.HrModuleConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', @@ -54,7 +57,8 @@ ROOT_URLCONF = 'timefall.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [BASE_DIR / 'templates'] + 'DIRS': [BASE_DIR / 'templates', + BASE_DIR / 'hr_module/templates'] , 'APP_DIRS': True, 'OPTIONS': { @@ -78,6 +82,7 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': BASE_DIR / 'db.sqlite3', + 'STRING': 'sqlite:///' + str(BASE_DIR / 'db.sqlite3') } } @@ -119,3 +124,11 @@ USE_TZ = True # https://docs.djangoproject.com/en/3.1/howto/static-files/ STATIC_URL = '/static/' + +STATICFILES_DIRS = [ + os.path.join(BASE_DIR, 'static'), + +] + + +TMP_FILE_STORAGE = os.path.join(BASE_DIR, 'temp/') \ No newline at end of file