Skip to content
This repository has been archived by the owner on Jan 27, 2018. It is now read-only.

Dev jonas #39

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ __pycache__
/build
/dist
/*.egg-info
.idea/
25 changes: 13 additions & 12 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,20 @@
long_descr = f.read().decode("utf-8")

setup(
name = "studip-client",
packages = ["studip"],
entry_points = {
"console_scripts": [ "studip = studip.application:main" ]
name="studip-client",
packages=["studip"],
entry_points={
"console_scripts": ["studip = studip.application:main"]
},
include_package_data = True,
install_requires = [
include_package_data=True,
install_requires=[
"requests",
"appdirs"
"appdirs",
"bs4"
],
version = version,
description = "CLI Client for the Stud.IP University Access Portal",
long_description = long_descr,
author = "Fabian Knorr",
url = "https://github.com/fknorr/studip-client"
version=version,
description="CLI Client for the Stud.IP University Access Portal",
long_description=long_descr,
author="Fabian Knorr",
url="https://github.com/fknorr/studip-client"
)
102 changes: 46 additions & 56 deletions studip/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def complete(self):

class Course:
def __init__(self, id, semester=None, number=None, name=None, abbrev=None, type=None,
type_abbrev=None, sync=None):
type_abbrev=None, sync=None):
self.id = id
self.semester = semester
self.number = number
Expand Down Expand Up @@ -58,9 +58,9 @@ def complete(self):

class File:
def __init__(self, id, course=None, course_semester=None, course_name=None, course_abbrev=None,
course_type=None, course_type_abbrev=None, path=None, name=None, extension=None,
author=None, description=None, remote_date=None, copyrighted=False, local_date=None,
version=None):
course_type=None, course_type_abbrev=None, path=None, name=None, extension=None,
author=None, description=None, description_url=None, remote_date=None, copyrighted=False,
local_date=None, version=None):
self.id = id
self.course = course
self.course_semester = course_semester
Expand All @@ -73,6 +73,7 @@ def __init__(self, id, course=None, course_semester=None, course_name=None, cour
self.extension = extension
self.author = author
self.description = description
self.description_url = description_url
self.remote_date = remote_date
self.copyrighted = copyrighted
self.local_date = local_date
Expand All @@ -85,7 +86,7 @@ def course_abbrev(self):
@property
def course_type_abbrev(self):
return self._course_type_abbrev if self._course_type_abbrev \
else abbreviate_course_type(self.course_type)
else abbreviate_course_type(self.course_type)

def complete(self):
return self.id and self.course and self.path and self.name and self.remote_date
Expand All @@ -104,7 +105,7 @@ def complete(self):

class View:
def __init__(self, id, name=None, format="{course}/{type}/{short-path}/{name}{ext}",
base=None, escape=EscapeMode.Similar, charset=Charset.Unicode):
base=None, escape=EscapeMode.Similar, charset=Charset.Unicode):
self.id = id
self.name = name
self.format = format
Expand All @@ -119,6 +120,7 @@ def complete(self):
class DatabaseVersionError(Exception):
pass


class QueryError(Exception):
pass

Expand All @@ -135,7 +137,7 @@ def connect(self):
connect(self)
db_version, = self.query("PRAGMA user_version", expected_rows=1)[0]
if db_version < self.schema_version:
if db_version in [ 9, 11 ]:
if db_version in [9, 11]:
# Disconnect and reconnect to create a backup
self.conn.close()
base_name, ext = os.path.splitext(file_name)
Expand All @@ -149,22 +151,22 @@ def connect(self):
self.query_script_file("migrate-11-12.sql")

print("Migrated database from version {} to {}, backup saved to {}".format(
db_version, self.schema_version, backup_file))
db_version, self.schema_version, backup_file))
elif db_version != 0:
print("Could not migrate database. Run \"studip clear-cache\" to reset DB. " \
+ "This will reset all views.")
+ "This will reset all views.")
self.conn.close()
raise DatabaseVersionError()

self.query("PRAGMA user_version = " + str(self.schema_version), expected_rows=0)

if db_version == 0: # Empty database
if db_version == 0: # Empty database
# Create all tables, views and triggers
self.query_script_file("setup.sql")
self.add_view(View(0, "default"))
elif db_version > self.schema_version:
print("The client database was created by a more recent version of studip-client" \
+ " - please update or run \"studip clear-cache\"")
+ " - please update or run \"studip clear-cache\"")
self.conn.close()
raise DatabaseVersionError()

Expand All @@ -176,44 +178,44 @@ def query(self, sql, expected_rows=-1, *args, **kwargs):
else:
raise ValueError("Pass either positional or keyword arguments")
elif kwargs:
cursor.execute(sql, dict(**kwargs))
try:
cursor.execute(sql, dict(**kwargs))
except sqlite3.IntegrityError:
pass
else:
cursor.execute(sql)

if expected_rows != 0:
rows = cursor.fetchmany(expected_rows)
if len(rows) < expected_rows:
raise QueryError("Expected at least {} rows, got {}".format(
expected_rows, len(rows)))
expected_rows, len(rows)))
return rows

def query_script(self, sql):
return self.conn.cursor().executescript(sql)


def query_script_file(self, name):
script_dir = os.path.join(os.path.dirname(os.path.realpath(__file__)), "sql")
with open(os.path.join(script_dir, name), "r") as file:
init_script = file.read()
return self.query_script(init_script)


def query_multiple(self, sql, args):
self.conn.cursor().executemany(sql, args)


def update_semester_list(self, semesters):
self.query_multiple("""
INSERT OR REPLACE INTO semesters (id, name, ord)
VALUES (:id, :name, :order)
""", (s.__dict__ for s in semesters))


def list_courses(self, full=False, select_sync_yes=True, select_sync_metadata_only=True,
select_sync_no=True):
sync_modes = [ str(int(enum)) for enable, enum in [ (select_sync_yes, SyncMode.Full),
(select_sync_metadata_only, SyncMode.Metadata), (select_sync_no, SyncMode.NoSync) ]
if enable ]
select_sync_no=True):
sync_modes = [str(int(enum)) for enable, enum in [(select_sync_yes, SyncMode.Full),
(select_sync_metadata_only, SyncMode.Metadata),
(select_sync_no, SyncMode.NoSync)]
if enable]

if full:
rows = self.query("""
Expand All @@ -223,16 +225,15 @@ def list_courses(self, full=False, select_sync_yes=True, select_sync_metadata_on
WHERE c.sync IN ({})
ORDER BY c.name, c.type;
""".format(", ".join(sync_modes)))
return [ Course(i, s, n, a, b, t, u, SyncMode(sync))
for i, s, n, a, b, t, u, sync in rows ]
return [Course(i, s, n, a, b, t, u, SyncMode(sync))
for i, s, n, a, b, t, u, sync in rows]
else:
rows = self.query("""
SELECT id FROM courses
WHERE sync IN ({})
ORDER BY name;
""".format(", ".join(sync_modes)))
return [ id for (id,) in rows ]

return [id for (id,) in rows]

def get_course_details(self, course_id):
rows = self.query("""
Expand All @@ -243,18 +244,16 @@ def get_course_details(self, course_id):
""", id=course_id, expected_rows=1)
semester, number, name, abbrev, type, type_abbrev, sync = rows[0]
return Course(id=course_id, semester=semester, number=number, name=name,
abbrev=abbrev, type=type, type_abbrev=type_abbrev, sync=SyncMode(sync))

abbrev=abbrev, type=type, type_abbrev=type_abbrev, sync=SyncMode(sync))

def add_course(self, course):
self.query("""
INSERT INTO courses (id, semester, number, name, abbrev, type, type_abbrev, sync)
VALUES (:id, (SELECT id FROM semesters WHERE name = :sem), :num, :name, :abbrev,
:type, :tabbrev, :sync);
""", id=course.id, sem=course.semester, num=course.number, name=course.name,
abbrev=course._abbrev, type=course.type, tabbrev=course._type_abbrev,
sync=int(course.sync), expected_rows=0)

abbrev=course._abbrev, type=course.type, tabbrev=course._type_abbrev,
sync=int(course.sync), expected_rows=0)

def update_course(self, course):
self.query("""
Expand All @@ -263,23 +262,22 @@ def update_course(self, course):
sync=:sync
WHERE id=:id;
""", id=course.id, num=course.number, name=course.name, abbrev=course.abbrev,
type=course.type, type_abbrev=course.type_abbrev, sync=int(course.sync),
expected_rows=0)

type=course.type, type_abbrev=course.type_abbrev, sync=int(course.sync),
expected_rows=0)

def delete_course(self, course):
self.query("""
DELETE FROM courses
WHERE id = :id;
""", id=course.id, expected_rows=0)


def list_files(self, full=False, select_sync_yes=True, select_sync_metadata_only=True,
select_sync_no=True):
select_sync_no=True):
Mode = SyncMode
sync_modes = [ str(int(enum)) for enable, enum in [ (select_sync_yes, SyncMode.Full),
(select_sync_metadata_only, SyncMode.Metadata), (select_sync_no, SyncMode.NoSync) ]
if enable ]
sync_modes = [str(int(enum)) for enable, enum in [(select_sync_yes, SyncMode.Full),
(select_sync_metadata_only, SyncMode.Metadata),
(select_sync_no, SyncMode.NoSync)]
if enable]

if full:
rows = self.query("""
Expand All @@ -290,8 +288,8 @@ def list_files(self, full=False, select_sync_yes=True, select_sync_metadata_only
WHERE sync IN ({});
""".format(", ".join(sync_modes)))
# Path is encoded as the string representation of a python list
return [ File(i, j, s, c, b, o, u, ast.literal_eval(path), n, e, a, d, t, y, l, v)
for i, j, s, c, b, o, u, path, n, e, a, d, t, y, l, v in rows ]
return [File(i, j, s, c, b, o, u, ast.literal_eval(path), n, e, a, d, t, y, l, v)
for i, j, s, c, b, o, u, path, n, e, a, d, t, y, l, v in rows]

else:
rows = self.query("""
Expand All @@ -301,7 +299,6 @@ def list_files(self, full=False, select_sync_yes=True, select_sync_metadata_only
""".format(", ".join(sync_modes)))
return [id for (id,) in rows]


def create_parent_for_file(self, file):
rows = self.query("""
SELECT root FROM courses
Expand All @@ -327,17 +324,15 @@ def query_subdirectory():

return parent


def add_file(self, file):
parent = self.create_parent_for_file(file)
self.query("""
INSERT INTO files (id, folder, name, extension, author, description, remote_date,
copyrighted, local_date, version)
VALUES (:id, :par, :name, :ext, :auth, :descr, :creat, :copy, :local, 0);
""", id=file.id, par=parent, name=file.name, ext=file.extension, auth=file.author,
descr=file.description, creat=file.remote_date, copy=file.copyrighted,
local=file.local_date, expected_rows=0)

descr=file.description, creat=file.remote_date, copy=file.copyrighted,
local=file.local_date, expected_rows=0)

def update_file(self, file):
parent = self.create_parent_for_file(file)
Expand All @@ -348,38 +343,35 @@ def update_file(self, file):
local_date = :local, version = version + 1
WHERE id = :id;
""", id=file.id, par=parent, name=file.name, ext=file.extension, auth=file.author,
descr=file.description, creat=file.remote_date, copy=file.copyrighted,
local=file.local_date, expected_rows=0)
descr=file.description, creat=file.remote_date, copy=file.copyrighted,
local=file.local_date, expected_rows=0)
self.query("""
DELETE FROM checkouts
WHERE file=:id
""", id=file.id, expected_rows=0)


def update_file_local_date(self, file):
self.query("""
UPDATE files
SET local_date = :local
WHERE id = :id
""", id=file.id, local=file.local_date, expected_rows=0)


def list_views(self, full=False):
if full:
rows = self.query("""
SELECT id, name, format, base, esc_mode, charset
FROM views
ORDER BY name;
""")
return [ View(i, n, f, b, EscapeMode(e), Charset(c)) for i, n, f, b, e, c in rows ]
return [View(i, n, f, b, EscapeMode(e), Charset(c)) for i, n, f, b, e, c in rows]
else:
rows = self.query("""
SELECT id
FROM views
ORDER BY name;
""")
return [ id for id, in rows ]

return [id for id, in rows]

def get_view_details(self, id):
rows = self.query("""
Expand All @@ -390,13 +382,12 @@ def get_view_details(self, id):
n, f, e, c = rows[0]
return View(id, n, f, EscapeMode(e), Charset(c))


def add_view(self, view):
self.query("""
INSERT INTO views (id, name, format, base, esc_mode, charset)
VALUES (:id, :name, :fmt, :base, :esc, :char)
""", id=view.id, name=view.name, fmt=view.format, base=view.base, esc=view.escape,
char=view.charset, expected_rows=0)
char=view.charset, expected_rows=0)

def remove_view(self, id):
self.query("""
Expand All @@ -409,7 +400,7 @@ def list_checkouts(self, view_id):
SELECT file FROM checkouts
WHERE view=:view
""", view=view_id)
return [ id for id, in rows ]
return [id for id, in rows]

def add_checkout(self, view_id, file_id):
self.query("""
Expand All @@ -425,4 +416,3 @@ def reset_checkouts(self, view_id):

def commit(self):
self.conn.commit()

Loading