2012-11-30 17:52:32 +00:00
|
|
|
import csv
|
2018-05-25 13:49:00 +00:00
|
|
|
import uuid
|
2020-10-07 15:18:43 +00:00
|
|
|
|
|
|
|
from datetime import date, datetime, time, timedelta
|
2012-11-30 17:52:32 +00:00
|
|
|
from io import StringIO
|
2020-10-07 15:18:43 +00:00
|
|
|
|
|
|
|
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile, status
|
|
|
|
from sqlalchemy import and_, bindparam, exists, select
|
2018-05-25 13:49:00 +00:00
|
|
|
from sqlalchemy.dialects.postgresql import insert as pg_insert
|
2020-10-07 14:07:28 +00:00
|
|
|
from sqlalchemy.exc import SQLAlchemyError
|
2020-05-14 16:19:22 +00:00
|
|
|
from sqlalchemy.orm import Session
|
2018-05-25 13:49:00 +00:00
|
|
|
|
2020-10-07 14:07:28 +00:00
|
|
|
from ..core.security import get_current_active_user as get_user
|
|
|
|
from ..db.session import SessionLocal
|
|
|
|
from ..models.master import Employee
|
|
|
|
from ..models.voucher import Fingerprint
|
|
|
|
from ..routers import get_lock_info
|
2020-10-07 15:18:43 +00:00
|
|
|
from ..schemas.auth import UserToken
|
|
|
|
|
2019-04-06 04:13:12 +00:00
|
|
|
|
2020-05-08 04:52:25 +00:00
|
|
|
router = APIRouter()
|
|
|
|
|
|
|
|
|
2020-10-07 14:07:28 +00:00
|
|
|
# Dependency
|
|
|
|
def get_db() -> Session:
|
|
|
|
try:
|
|
|
|
db = SessionLocal()
|
|
|
|
yield db
|
|
|
|
finally:
|
|
|
|
db.close()
|
2018-05-25 13:49:00 +00:00
|
|
|
|
2020-10-07 14:07:28 +00:00
|
|
|
|
|
|
|
@router.post("")
|
|
|
|
def upload_prints(
|
2020-10-07 16:59:24 +00:00
|
|
|
db: Session = Depends(get_db),
|
|
|
|
fingerprints: UploadFile = File(None),
|
|
|
|
user: UserToken = Depends(get_user),
|
2020-10-07 14:07:28 +00:00
|
|
|
):
|
|
|
|
try:
|
|
|
|
start, finish = get_lock_info(db)
|
|
|
|
employees = {}
|
|
|
|
for id_, code in db.query(Employee.id, Employee.code).all():
|
|
|
|
employees[code] = id_
|
|
|
|
file_data = read_file(fingerprints)
|
|
|
|
prints = [d for d in fp(file_data, employees) if start <= d["date"] <= finish]
|
2020-10-07 15:18:43 +00:00
|
|
|
paged_data = [prints[i : i + 100] for i in range(0, len(prints), 100)]
|
2020-10-07 14:07:28 +00:00
|
|
|
for i, page in enumerate(paged_data):
|
|
|
|
print(f"Processing page {i} of {len(paged_data)}")
|
|
|
|
db.execute(get_query(9.4), page)
|
|
|
|
db.commit()
|
|
|
|
return {}
|
|
|
|
except SQLAlchemyError as e:
|
|
|
|
db.rollback()
|
|
|
|
raise HTTPException(
|
2020-10-07 15:18:43 +00:00
|
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
|
|
detail=str(e),
|
2020-10-07 14:07:28 +00:00
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
db.rollback()
|
|
|
|
raise
|
2018-05-25 13:49:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
def get_query(version):
|
|
|
|
if version == 9.5:
|
2019-04-06 04:13:12 +00:00
|
|
|
return (
|
|
|
|
pg_insert(Fingerprint)
|
|
|
|
.values(
|
2020-10-07 15:18:43 +00:00
|
|
|
{
|
|
|
|
"FingerprintID": bindparam("id"),
|
|
|
|
"EmployeeID": bindparam("employee_id"),
|
|
|
|
"Date": bindparam("date"),
|
|
|
|
}
|
2019-04-06 04:13:12 +00:00
|
|
|
)
|
|
|
|
.on_conflict_do_nothing()
|
|
|
|
)
|
2018-05-25 13:49:00 +00:00
|
|
|
else:
|
2020-10-07 16:59:24 +00:00
|
|
|
sel = select(
|
|
|
|
[bindparam("id"), bindparam("employee_id"), bindparam("date")]
|
|
|
|
).where(
|
2018-05-25 13:49:00 +00:00
|
|
|
~exists([Fingerprint.id]).where(
|
2020-10-07 15:18:43 +00:00
|
|
|
and_(
|
|
|
|
Fingerprint.employee_id == bindparam("employee_id"),
|
|
|
|
Fingerprint.date == bindparam("date"),
|
|
|
|
)
|
2018-05-25 13:49:00 +00:00
|
|
|
)
|
|
|
|
)
|
2019-04-06 04:13:12 +00:00
|
|
|
return Fingerprint.__table__.insert().from_select(
|
|
|
|
[Fingerprint.id, Fingerprint.employee_id, Fingerprint.date], sel
|
|
|
|
)
|
2012-11-30 17:52:32 +00:00
|
|
|
|
|
|
|
|
2020-10-07 14:07:28 +00:00
|
|
|
def read_file(input_file: UploadFile):
|
2012-11-30 17:52:32 +00:00
|
|
|
input_file.seek(0)
|
2019-04-06 04:13:12 +00:00
|
|
|
output = bytearray()
|
2012-11-30 17:52:32 +00:00
|
|
|
while 1:
|
|
|
|
data = input_file.read(2 << 16)
|
|
|
|
if not data:
|
|
|
|
break
|
2019-04-06 04:13:12 +00:00
|
|
|
output.extend(data)
|
|
|
|
if output[0:3] == b"\xef\xbb\xbf":
|
|
|
|
encoding = "utf-8"
|
|
|
|
elif output[0:2] == b"\xfe\xff" or output[0:2] == b"\xff\xfe":
|
|
|
|
encoding = "utf-16"
|
|
|
|
else:
|
|
|
|
encoding = "ascii"
|
|
|
|
# raise ValidationError("The encoding of the attendance file is not correct")
|
|
|
|
return StringIO(output.decode(encoding))
|
2012-11-30 17:52:32 +00:00
|
|
|
|
|
|
|
|
2019-04-06 04:13:12 +00:00
|
|
|
def fp(file_data, employees):
|
2018-05-25 13:49:00 +00:00
|
|
|
fingerprints = []
|
2019-04-06 04:13:12 +00:00
|
|
|
reader = csv.reader(file_data, delimiter="\t")
|
|
|
|
header = next(reader, None)
|
|
|
|
employee_column = 2
|
|
|
|
date_column = len(header) - 1
|
|
|
|
date_format = "%Y/%m/%d %H:%M:%S" if date_column == 6 else "%Y-%m-%d %H:%M:%S"
|
2018-05-25 13:49:00 +00:00
|
|
|
for row in reader:
|
|
|
|
try:
|
2019-04-06 04:13:12 +00:00
|
|
|
employee_code = int(row[employee_column]) # EnNo
|
|
|
|
date = datetime.datetime.strptime(row[date_column], date_format)
|
2018-05-25 13:49:00 +00:00
|
|
|
if employee_code in employees.keys():
|
2019-04-06 04:13:12 +00:00
|
|
|
fingerprints.append(
|
2020-10-07 15:18:43 +00:00
|
|
|
{
|
|
|
|
"id": uuid.uuid4(),
|
|
|
|
"employee_id": employees[employee_code],
|
|
|
|
"date": date,
|
|
|
|
}
|
2019-04-06 04:13:12 +00:00
|
|
|
)
|
2018-05-25 13:49:00 +00:00
|
|
|
except ValueError:
|
|
|
|
continue
|
|
|
|
return fingerprints
|
|
|
|
|
|
|
|
|
|
|
|
# # Upsert using subquery
|
|
|
|
# sel = select([literal(uuid.uuid4()), literal(employee_id), literal(date)]).where(
|
|
|
|
# ~exists([Fingerprint.id]).where(
|
|
|
|
# and_(
|
|
|
|
# Fingerprint.employee_id == employee_id, Fingerprint.date == date
|
|
|
|
# )
|
|
|
|
# )
|
|
|
|
# )
|
|
|
|
# return Fingerprint.__table__.insert().from_select([Fingerprint.id, Fingerprint.employee_id, Fingerprint.date], sel)
|
2016-02-29 18:11:08 +00:00
|
|
|
|
2012-11-30 20:00:55 +00:00
|
|
|
|
2020-05-14 16:19:22 +00:00
|
|
|
def get_prints(employee_id: uuid.UUID, date_: date, db: Session):
|
2019-04-06 04:13:12 +00:00
|
|
|
prints = (
|
2020-05-14 16:19:22 +00:00
|
|
|
db.query(Fingerprint)
|
2019-04-06 04:13:12 +00:00
|
|
|
.filter(Fingerprint.employee_id == employee_id)
|
2020-05-14 16:19:22 +00:00
|
|
|
.filter(Fingerprint.date >= datetime.combine(date_, time(hour=7)))
|
2020-10-07 16:59:24 +00:00
|
|
|
.filter(
|
|
|
|
Fingerprint.date < datetime.combine(date_ + timedelta(days=1), time(hour=7))
|
|
|
|
)
|
2019-04-06 04:13:12 +00:00
|
|
|
.order_by(Fingerprint.date)
|
|
|
|
.all()
|
|
|
|
)
|
2012-11-30 20:00:55 +00:00
|
|
|
|
|
|
|
last = None
|
|
|
|
for i in range(len(prints), 0, -1):
|
|
|
|
item = prints[i - 1].date
|
2020-05-14 16:19:22 +00:00
|
|
|
if last is not None and last - item < timedelta(minutes=10):
|
2012-11-30 20:00:55 +00:00
|
|
|
prints.remove(prints[i - 1])
|
|
|
|
else:
|
|
|
|
last = item
|
|
|
|
|
|
|
|
if len(prints) == 0:
|
2020-05-14 16:19:22 +00:00
|
|
|
hours_worked, full_day = "", None
|
2012-11-30 20:00:55 +00:00
|
|
|
elif len(prints) == 2:
|
2020-05-14 16:19:22 +00:00
|
|
|
time_worked = prints[1].date - prints[0].date
|
|
|
|
hours_worked, full_day = working_hours(time_worked)
|
2012-11-30 20:00:55 +00:00
|
|
|
elif len(prints) == 4:
|
2020-10-07 16:59:24 +00:00
|
|
|
time_worked = (prints[1].date - prints[0].date) + (
|
|
|
|
prints[3].date - prints[2].date
|
|
|
|
)
|
2020-05-14 16:19:22 +00:00
|
|
|
hours_worked, full_day = working_hours(time_worked)
|
2012-11-30 20:00:55 +00:00
|
|
|
else:
|
2020-05-14 16:19:22 +00:00
|
|
|
hours_worked, full_day = "Error", False
|
2019-04-06 04:13:12 +00:00
|
|
|
return (
|
|
|
|
", ".join([x.date.strftime("%H:%M") for x in prints]) + " ",
|
2020-05-14 16:19:22 +00:00
|
|
|
hours_worked,
|
|
|
|
full_day,
|
2019-04-06 04:13:12 +00:00
|
|
|
)
|
2012-11-30 20:00:55 +00:00
|
|
|
|
|
|
|
|
2020-05-14 16:19:22 +00:00
|
|
|
def working_hours(delta: timedelta):
|
2012-11-30 20:00:55 +00:00
|
|
|
minutes = (delta.seconds // 60) % 60
|
|
|
|
minutes = int(5 * round(float(minutes) / 5))
|
|
|
|
hours = delta.seconds // 3600
|
2020-05-14 16:19:22 +00:00
|
|
|
hours_worked = str(hours).zfill(2) + ":" + str(minutes).zfill(2)
|
|
|
|
return hours_worked, delta.seconds >= 60 * 60 * 9 # 9hrs
|