<commit_before>from codecs import encode
import json
import random
import re

import discord
from discord.ext import commands
from lxml import etree

from utils import checks

class Default:
    def __init__(self, bot):
        self.bot = bot
        with open('data/why.txt', encoding='utf8') as file:
            self.questions = tuple(file.readlines())

    @commands.command(aliases=['p'])
    async def ping(self, ctx):
        """Pong.

        Responds to the ping with "pong."
        """
        msg = await ctx.send('pong')
        delta = (msg.created_at - ctx.message.created_at).total_seconds()
        await msg.edit(content=f'{msg.content}\nTime to respond: '
                       f'{delta:.3f} seconds')

    @commands.command()
    async def choose(self, ctx, *options):
        """Choose between some options. Use quotes if they have spaces."""
        await ctx.send(random.choice(options))

    @commands.command()
    async def why(self, ctx):
        """Asks a question."""
        await ctx.send(random.choice(self.questions))

    @commands.group()
    async def xkcd(self, ctx, *, inp=''):
        """Commands for getting xkcd comics"""
        async with ctx.typing():
            url = 'https://xkcd.com/info.0.json'
            async with self.bot.session.get(url) as resp:
                self.xkcd_data = json.loads(await resp.text())
            if inp == 'random':
                await ctx.invoke(self.random)
            elif inp in ('latest', 'current'):
                await ctx.invoke(self.latest)
            else:
                if inp == '':
                    await ctx.invoke(self.random)
                else:
                    await ctx.invoke(self.comic, inp=inp)

    @xkcd.command()
    async def random(self, ctx):
        """Gets a random xkcd comic."""
        number = random.randint(1, self.xkcd_data['num'])
        url = f'https://xkcd.com/{number}/info.0.json'
        async with self.bot.session.get(url) as resp:
            data = json.loads(await resp.text())
        await ctx.send(embed=format_comic(data))

    @xkcd.command()
    async def latest(self, ctx):
        """Gets the latest xkcd comic."""
        await ctx.send(embed=format_comic(self.xkcd_data))

    @xkcd.command()
    async def comic(self, ctx, *, inp):
        """Gets an xkcd comic by number or content."""
        try:
            number = int(inp)
        except ValueError:
            url = 'https://duckduckgo.com/html/'
            params = {'q': f'{inp} xkcd'}
            async with self.bot.session.get(url, params=params) as resp:
                text = await resp.text()
            match = re.search(r'xkcd.com/(\d+)', text)
            if match:
                number = match.groups()[0]
            else:
                await ctx.send('No comic found.')

        url = f'https://xkcd.com/{number}/info.0.json'
        async with self.bot.session.get(url) as resp:
            data = json.loads(await resp.text())
        await ctx.send(embed=format_comic(data))

    @commands.group(aliases=['str', 's'])
    async def string(self, ctx):
        """Commands for doing stuff to strings."""
        if ctx.invoked_subcommand is None:
            await ctx.send('Invalid command passed. '
                           f'Try "{ctx.prefix}help string"')

    @string.command(aliases=['rev', 'r'])
    async def reverse(self, ctx, *, inp):
        """Reverse a string."""
        await ctx.send(inp[::-1])

    @string.command(aliases=['rot'])
    async def rot13(self, ctx, *, inp):
        """Apply rot13 to a string.

        Uses the shift cipher with key 13."""
        await ctx.send(encode(inp, 'rot_13'))

    @commands.command(hidden=True)
    @checks.is_owner()
    async def sudo(self, ctx, *, inp):
        await ctx.send('Operation successful.')

    @sudo.error
    async def sudo_error(self, exception, ctx):
        if isinstance(exception, commands.errors.CheckFailure):
            await ctx.send('Unable to lock /var/lib/dpkg/, are you root?')
        else:
            await self.bot.handle_error(exception, ctx)

def format_comic(data):
    embed = discord.Embed()
    embed.title = data['title']
    embed.set_image(url=data['img'])
    embed.set_footer(text=data['alt'])
    embed.url = f"https://www.xkcd.com/{data['num']}"
    return embed

def setup(bot):
    bot.add_cog(Default(bot))
<commit_msg>Edited pint<commit_after>from codecs import encode
import json
import random
import re
import time

import discord
from discord.ext import commands
from lxml import etree

from utils import checks

class Default:
    def __init__(self, bot):
        self.bot = bot
        with open('data/why.txt', encoding='utf8') as file:
            self.questions = tuple(file.readlines())

    @commands.command(aliases=['p'])
    async def ping(self, ctx):
        """Ping command, taken from somewhere idk"""
        msg = await ctx.send("Pong. :ping_pong:")

        before = time.monotonic()
        await (await self.bot.ws.ping())
        after = time.monotonic()
        ping_time = (after - before) * 1000

        await msg.edit(content=f'{msg.content} **{ping_time:.0f}ms**')

    @commands.command()
    async def choose(self, ctx, *options):
        """Choose between some options. Use quotes if they have spaces."""
        await ctx.send(random.choice(options))

    @commands.command()
    async def why(self, ctx):
        """Asks a question."""
        await ctx.send(random.choice(self.questions))

    @commands.group()
    async def xkcd(self, ctx, *, inp=''):
        """Commands for getting xkcd comics"""
        async with ctx.typing():
            url = 'https://xkcd.com/info.0.json'
            async with self.bot.session.get(url) as resp:
                self.xkcd_data = json.loads(await resp.text())
            if inp == 'random':
                await ctx.invoke(self.random)
            elif inp in ('latest', 'current'):
                await ctx.invoke(self.latest)
            else:
                if inp == '':
                    await ctx.invoke(self.random)
                else:
                    await ctx.invoke(self.comic, inp=inp)

    @xkcd.command()
    async def random(self, ctx):
        """Gets a random xkcd comic."""
        number = random.randint(1, self.xkcd_data['num'])
        url = f'https://xkcd.com/{number}/info.0.json'
        async with self.bot.session.get(url) as resp:
            data = json.loads(await resp.text())
        await ctx.send(embed=format_comic(data))

    @xkcd.command()
    async def latest(self, ctx):
        """Gets the latest xkcd comic."""
        await ctx.send(embed=format_comic(self.xkcd_data))

    @xkcd.command()
    async def comic(self, ctx, *, inp):
        """Gets an xkcd comic by number or content."""
        try:
            number = int(inp)
        except ValueError:
            url = 'https://duckduckgo.com/html/'
            params = {'q': f'{inp} xkcd'}
            async with self.bot.session.get(url, params=params) as resp:
                text = await resp.text()
            match = re.search(r'xkcd.com/(\d+)', text)
            if match:
                number = match.groups()[0]
            else:
                await ctx.send('No comic found.')

        url = f'https://xkcd.com/{number}/info.0.json'
        async with self.bot.session.get(url) as resp:
            data = json.loads(await resp.text())
        await ctx.send(embed=format_comic(data))

    @commands.group(aliases=['str', 's'])
    async def string(self, ctx):
        """Commands for doing stuff to strings."""
        if ctx.invoked_subcommand is None:
            await ctx.send('Invalid command passed. '
                           f'Try "{ctx.prefix}help string"')

    @string.command(aliases=['rev', 'r'])
    async def reverse(self, ctx, *, inp):
        """Reverse a string."""
        await ctx.send(inp[::-1])

    @string.command(aliases=['rot'])
    async def rot13(self, ctx, *, inp):
        """Apply rot13 to a string.

        Uses the shift cipher with key 13."""
        await ctx.send(encode(inp, 'rot_13'))

    @commands.command(hidden=True)
    @checks.is_owner()
    async def sudo(self, ctx, *, inp):
        await ctx.send('Operation successful.')

    @sudo.error
    async def sudo_error(self, exception, ctx):
        if isinstance(exception, commands.errors.CheckFailure):
            await ctx.send('Unable to lock /var/lib/dpkg/, are you root?')
        else:
            await self.bot.handle_error(exception, ctx)

def format_comic(data):
    embed = discord.Embed()
    embed.title = data['title']
    embed.set_image(url=data['img'])
    embed.set_footer(text=data['alt'])
    embed.url = f"https://www.xkcd.com/{data['num']}"
    return embed

def setup(bot):
    bot.add_cog(Default(bot))
<|endoftext|>


<commit_before>try:
	import appscript
except ImportError:
	import sys
	print >> sys.stderr,'Requires appscript module'
	print >> sys.stderr,'http://appscript.sourceforge.net/py-appscript/install.html'
	print >> sys.stderr,'"sudo easy_install appscript"'
	exit(1)

def rprint(items,tabs = 0):
	for item in items:
		print '\t'*tabs,item
		if isinstance(item,List):
			rprint(item.tasks(),(tabs+1))
		if isinstance(item,Folder):
			rprint(item.groups(),(tabs+1))

class Application(object):
	def __init__(self):
		self.thl = appscript.app('The Hit List')
	def inbox(self):
		return List(self.thl.inbox)
	def today(self):
		return List(self.thl.today_list)
	def upcoming(self):
		return List(self.thl.upcoming_list)
	def folders(self):
		return Folder(self.thl.folders_group)
	def tags(self):
		tags = []
		for tag in self.thl.tags_group.tags():
			tags.append(Tag(tag))
		return tags
	def help(self):
		return self.thl.help()

	def find_list(self,name):
		return self.folders().find_list(name)
	def find_folder(self,name):
		return self.folders().find_folder(name)
	def find_task(self,name):
		return self.folders().find_task(name)

class Task(object):
	def __init__(self,obj=None):
		self._raw = obj
		self._properties = [
			'id','title','notes',
			'start_date','due_date',
			'estimated_time','actual_time',
			'archived_date','canceled_date',
			'modified_date','priority',
			'archived','canceled','completed',
			'url','completed_date','repeating'
			]
		if obj:
			for prop in self._properties:
				func = getattr(obj,prop)
				value = func()
				if value == appscript.k.missing_value: value = None
				self.__setattr__(prop,value)
		else:
			self.id = None
			self.title = None
	def print_obj(self):
		for prop in self._properties:
			value = self.__dict__.get(prop,None)
			if value: print prop,':',value
	def format_obj(self):
		obj = {}
		if self.__dict__.get('title',None):
			obj[appscript.k.title] = unicode(self.title,'utf-8')
		if self.__dict__.get('notes',None):
			obj[appscript.k.notes] = unicode(self.notes,'utf-8')
		return obj
	def __repr__(self):
		return '<%s:%s:%s>'%(self.__class__,self.id,self.title)

class Group(object):
	def __init__(self,obj=None):
		self._raw = obj
		self._properties = [
			'id','name',
			'modified_date',
			'created_date'
			]
		if obj:
			for prop in self._properties:
				func = getattr(obj,prop)
				self.__setattr__(prop,func())
	def print_obj(self):
		for prop in self._properties:
			print prop,self.__getattribute__(prop)
	def __repr__(self):
		return '<%s:%s:%s>'%(self.__class__,self.id,self.name)
	def find_list(self,name):
		def _find_list(name,groups):
			for group in groups:
				if isinstance(group,Folder):
					result = _find_list(name,group.groups())
					if result is not None: return result
				if group.name == name: return group
			return None
		return _find_list(name,self.groups())
	def find_folder(self,name):
		def _find_folder(name,groups):
			for group in groups:
				if isinstance(group,Folder):
					if group.name == name: return group
					result = _find_folder(name,group.groups())
					if result is not None: return result
			return None
		return _find_folder(name,self.groups())

class List(Group):
	def tasks(self):
		tasks = []
		for task in self._raw.tasks():
			tasks.append(Task(task))
		return tasks
	def add_task(self,task):
		self._raw().make(
			new=appscript.k.task,
			with_properties=task.format_obj()
		)
	def format_obj(self):
		obj = {}
		if self.__dict__.get('name',None):
			obj[appscript.k.name] = self.name
		return obj
class Folder(Group):
	def groups(self):
		if not self._raw.groups: return []
		groups = []
		for group in self._raw.groups():
			tmpClass = group.class_()
			if tmpClass == appscript.k.folder:
				groups.append(Folder(group))
			elif tmpClass == appscript.k.smart_folder:
				continue
			elif tmpClass == appscript.k.list_:
				groups.append(List(group))
			else:
				print 'Error:',tmpClass
		return groups
	def add_list(self,list):
		self._raw().make(
			new=appscript.k.list_,
			with_properties=list.format_obj()
		)
class Smart_Folder(Group):
	pass
class Tag(Group):
	pass
<commit_msg>Simple documentation for the Application class New helper functions add_task(), add_list(), new_list(), new_task() Refactor rprint() and add it to the Task and Group classes<commit_after>try:
	import appscript
except ImportError:
	import sys
	print >> sys.stderr,'Requires appscript module'
	print >> sys.stderr,'http://appscript.sourceforge.net/py-appscript/install.html'
	print >> sys.stderr,'"sudo easy_install appscript"'
	exit(1)

def rprint(item,tabs = 0):
	'''Recursively print Task, Folder, or Group objects'''
	if isinstance(item,Task):
		print '\t'*tabs,'<Task>',item.title
	if isinstance(item,List):
		print '\t'*tabs,'<List>',item.name
		for task in item.tasks():
			rprint(task,(tabs+1))
	if isinstance(item,Folder):
		print '\t'*tabs,'<Folder>',item.name
		for group in item.groups():
			rprint(group,(tabs+1))

class Application(object):
	def __init__(self):
		self.thl = appscript.app('The Hit List')
	def inbox(self):
		'''Return the inbox List'''
		return List(self.thl.inbox)
	def today(self):
		'''Return the today List'''
		return List(self.thl.today_list)
	def upcoming(self):
		'''Return the upcoming List'''
		return List(self.thl.upcoming_list)
	def folders(self):
		'''Return the root Folder'''
		return Folder(self.thl.folders_group)
	def tags(self):
		'''Return a list of Tag objects'''
		tags = []
		for tag in self.thl.tags_group.tags():
			tags.append(Tag(tag))
		return tags

	def find_list(self,name):
		'''Recursivly find a List by name'''
		return self.folders().find_list(name)
	def find_folder(self,name):
		'''Recursivly find a Folder by name'''
		return self.folders().find_folder(name)
	def find_task(self,name):
		'''Recursivly find a Task by name'''
		return self.folders().find_task(name)

	def add_task(self,task):
		'''Add a Task to the inbox'''
		self.inbox().add_task(task)
	def add_list(self,list):
		'''Add a List to the root folder'''
		self.folders().add_list(list)
	def new_list(self,name):
		'''Add a list to the root folder with the specified name'''
		list = List()
		list.name = name
		self.add_list(list)
		return self.find_list(name)
	def new_task(self,title):
		'''Add a task to the inbox with the specified title'''
		task = Task()
		task.title = title
		self.inbox().add_task(task)

class Task(object):
	def __init__(self,obj=None):
		self._raw = obj
		self._properties = [
			'id','title','notes',
			'start_date','due_date',
			'estimated_time','actual_time',
			'archived_date','canceled_date',
			'modified_date','priority',
			'archived','canceled','completed',
			'url','completed_date','repeating'
			]
		if obj:
			for prop in self._properties:
				func = getattr(obj,prop)
				value = func()
				if value == appscript.k.missing_value: value = None
				self.__setattr__(prop,value)
		else:
			self.id = None
			self.title = None
	def print_obj(self):
		for prop in self._properties:
			value = self.__dict__.get(prop,None)
			if value: print prop,':',value
	def format_obj(self):
		obj = {}
		if self.__dict__.get('title',None):
			obj[appscript.k.title] = unicode(self.title,'utf-8')
		if self.__dict__.get('notes',None):
			obj[appscript.k.notes] = unicode(self.notes,'utf-8')
		return obj
	def __repr__(self):
		return '<%s:%s:%s>'%(self.__class__,self.id,self.title)
	def rprint(self):
		rprint(self)

class Group(object):
	def __init__(self,obj=None):
		self._raw = obj
		self._properties = [
			'id','name',
			'modified_date',
			'created_date'
			]
		if obj:
			for prop in self._properties:
				func = getattr(obj,prop)
				self.__setattr__(prop,func())
	def print_obj(self):
		for prop in self._properties:
			print prop,self.__getattribute__(prop)
	def __repr__(self):
		return '<%s:%s:%s>'%(self.__class__,self.id,self.name)
	def find_list(self,name):
		def _find_list(name,groups):
			for group in groups:
				if isinstance(group,Folder):
					result = _find_list(name,group.groups())
					if result is not None: return result
				if group.name == name: return group
			return None
		return _find_list(name,self.groups())
	def find_folder(self,name):
		def _find_folder(name,groups):
			for group in groups:
				if isinstance(group,Folder):
					if group.name == name: return group
					result = _find_folder(name,group.groups())
					if result is not None: return result
			return None
		return _find_folder(name,self.groups())
	def rprint(self):
		rprint(self)

class List(Group):
	def tasks(self):
		tasks = []
		for task in self._raw.tasks():
			tasks.append(Task(task))
		return tasks
	def add_task(self,task):
		self._raw().make(
			new=appscript.k.task,
			with_properties=task.format_obj()
		)
	def format_obj(self):
		obj = {}
		if self.__dict__.get('name',None):
			obj[appscript.k.name] = self.name
		return obj
class Folder(Group):
	def groups(self):
		if not self._raw.groups: return []
		groups = []
		for group in self._raw.groups():
			tmpClass = group.class_()
			if tmpClass == appscript.k.folder:
				groups.append(Folder(group))
			elif tmpClass == appscript.k.smart_folder:
				continue
			elif tmpClass == appscript.k.list_:
				groups.append(List(group))
			else:
				print 'Error:',tmpClass
		return groups
	def add_list(self,list):
		self._raw().make(
			new=appscript.k.list_,
			with_properties=list.format_obj()
		)
class Smart_Folder(Group):
	pass
class Tag(Group):
	pass
<|endoftext|>


<commit_before>from configparser import ConfigParser

from redis import StrictRedis
import ujson as json

from keywords import get_frog, get_keywords
from selderij import app
from tasks_master import insert_tweet

keywords = get_keywords()

config = ConfigParser()
config.read("tasks_workers.ini")
posprob_minimum = config["workers"].getfloat("posprob_minimum")

r = StrictRedis()
rt_cache_time = 60 * 60 * 6

@app.task
def find_keywords_and_groups(id_str, text, retweet_id_str):
    """Find the keywords and associated groups in the tweet."""
    # First check if retweets are already processed in the cache
    if retweet_id_str:
        key = "t:%s" % retweet_id_str
        rt = r.get(key)
        if rt:
            kw, groups, tokens = json.loads(rt)
            insert_tweet.apply_async((id_str, kw, groups, tokens), queue="master")
            r.expire(key, ex=rt_cache_time)
            return

    frog = get_frog()
    tokens = frog.process(text)  # a list of dictionaries with frog's analysis per token
    kw = []
    groups = []
    for t in tokens:
        lemma = t["lemma"].lower()
        k = keywords.get(lemma, None)
        if k is not None:
            if t["posprob"] > posprob_minimum:
                if not t["pos"].startswith(k.pos + "("):
                    continue
            kw.append(lemma)
            groups += k.groups
    kw, groups = list(set(kw)), list(set(groups))
    insert_tweet.apply_async((id_str, kw, groups, tokens), queue="master")

    # put retweets in the cache
    if retweet_id_str:
        data = [kw, groups, tokens]
        r.set(key, json.dumps(data), ex=rt_cache_time)
<commit_msg>database: recache fix<commit_after>from configparser import ConfigParser

from redis import StrictRedis
import ujson as json

from keywords import get_frog, get_keywords
from selderij import app
from tasks_master import insert_tweet

keywords = get_keywords()

config = ConfigParser()
config.read("tasks_workers.ini")
posprob_minimum = config["workers"].getfloat("posprob_minimum")

r = StrictRedis()
rt_cache_time = 60 * 60 * 6

@app.task
def find_keywords_and_groups(id_str, text, retweet_id_str):
    """Find the keywords and associated groups in the tweet."""
    # First check if retweets are already processed in the cache
    if retweet_id_str:
        key = "t:%s" % retweet_id_str
        rt = r.get(key)
        if rt:
            kw, groups, tokens = json.loads(rt)
            insert_tweet.apply_async((id_str, kw, groups, tokens), queue="master")
            r.expire(key, rt_cache_time)
            return

    frog = get_frog()
    tokens = frog.process(text)  # a list of dictionaries with frog's analysis per token
    kw = []
    groups = []
    for t in tokens:
        lemma = t["lemma"].lower()
        k = keywords.get(lemma, None)
        if k is not None:
            if t["posprob"] > posprob_minimum:
                if not t["pos"].startswith(k.pos + "("):
                    continue
            kw.append(lemma)
            groups += k.groups
    kw, groups = list(set(kw)), list(set(groups))
    insert_tweet.apply_async((id_str, kw, groups, tokens), queue="master")

    # put retweets in the cache
    if retweet_id_str:
        data = [kw, groups, tokens]
        r.set(key, json.dumps(data), ex=rt_cache_time)
<|endoftext|>


<commit_before>"""
Mapping between obograph-JSON format and networkx
"""

from ontobio.ontol import LogicalDefinition, PropertyChainAxiom
from ontobio.vocabulary.relations import map_legacy_pred
from prefixcommons.curie_util import expand_uri, contract_uri
from ontobio.util.scigraph_util import get_curie_map
from ontobio.config import get_config

import json
import networkx
import logging

class OboJsonMapper(object):
    def __init__(self,
                 digraph=None,
                 context=None):
        self.digraph = digraph
        self.context = context if context is not None else {}

    def add_obograph_digraph(
            self,
            og,
            node_type=None,
            predicates=None,
            xref_graph=None,
            logical_definitions=None,
            property_chain_axioms=None,
            parse_meta=True,
            reverse_edges=True,
            **args):
        """
        Converts a single obograph to Digraph edges and adds to an existing networkx DiGraph
        """
        digraph = self.digraph
        logging.info("NODES: {}".format(len(og['nodes'])))

        # if client passes an xref_graph we must parse metadata
        if xref_graph is not None:
            parse_meta = True

        for node in og['nodes']:
            is_obsolete = 'is_obsolete' in node and node['is_obsolete'] == 'true'
            if is_obsolete:
                continue
            if node_type is not None and ('type' not in node or node['type'] != node_type):
                continue
            id = self.contract_uri(node['id'])
            digraph.add_node(id, **node)
            if 'lbl' in node:
                digraph.node[id]['label'] = node['lbl']
            if parse_meta and 'meta' in node:
                if node['meta'] is None:
                    node['meta'] = {}
                meta = self.transform_meta(node['meta'])
                if xref_graph is not None and 'xrefs' in meta:
                    for x in meta['xrefs']:
                        xref_graph.add_edge(self.contract_uri(x['val']), id, source=id)
        logging.info("EDGES: {}".format(len(og['edges'])))
        for edge in og['edges']:
            sub = self.contract_uri(edge['sub'])
            obj = self.contract_uri(edge['obj'])
            pred = self.contract_uri(edge['pred'])
            pred = map_legacy_pred(pred)
            if pred == 'is_a':
                pred = 'subClassOf'
            if predicates is None or pred in predicates:
                meta = edge['meta'] if 'meta' in edge else {}
                if reverse_edges:
                    digraph.add_edge(obj, sub, pred=pred, **meta)
                else:
                    digraph.add_edge(sub, obj, pred=pred, **meta)

        if 'equivalentNodesSets' in og:
            nslist = og['equivalentNodesSets']
            logging.info("CLIQUES: {}".format(len(nslist)))
            for ns in nslist:
                equivNodeIds = ns['nodeIds']
                for i in ns['nodeIds']:
                    ix = self.contract_uri(i)
                    for j in ns['nodeIds']:
                        if i != j:
                            jx = self.contract_uri(j)
                            digraph.add_edge(ix, jx, pred='equivalentTo')
        if logical_definitions is not None and 'logicalDefinitionAxioms' in og:
            for a in og['logicalDefinitionAxioms']:
                ld = LogicalDefinition(self.contract_uri(a['definedClassId']),
                                       [self.contract_uri(x) for x in a['genusIds']],
                                       [(self.contract_uri(x['propertyId']),
                                         self.contract_uri(x['fillerId'])) for x in a['restrictions'] if x is not None])
                logical_definitions.append(ld)
        if property_chain_axioms is not None and 'propertyChainAxioms' in og:
            for a in og['propertyChainAxioms']:
                pca = PropertyChainAxiom(predicate_id=self.contract_uri(a['predicateId']),
                                         chain_predicate_ids=[self.contract_uri(x) for x in a['chainPredicateIds']])
                property_chain_axioms.append(pca)

    def transform_meta(self, meta):
        if 'basicPropertyValues' in meta:
            for x in meta['basicPropertyValues']:
                x['pred'] = self.contract_uri(x['pred'])
                x['val'] = self.contract_uri(x['val'])
        return meta

    def contract_uri(self, uri):
        if len(self.context.keys()) > 0:
            curies = contract_uri(uri, cmaps=[self.context])
            if len(curies) > 0:
                return sorted(curies, key=len)[0] # sort by length

        curies = sorted(contract_uri(uri), key=len) # Sort by length
        if len(curies) > 0:
            return curies[0]
        else:
            return uri

def convert_json_file(obographfile, **args):
    """
    Return a networkx MultiDiGraph of the ontologies
    serialized as a json string

    """
    file = open(obographfile, 'r')
    jsonstr = file.read()
    file.close()
    return convert_json_object(json.loads(jsonstr), **args)

def convert_json_object(obographdoc, reverse_edges=True, **args):
    """
    Return a networkx MultiDiGraph of the ontologies
    serialized as a json object

    """
    digraph = networkx.MultiDiGraph()
    xref_graph = networkx.MultiGraph()
    logical_definitions = []
    property_chain_axioms = []
    context = obographdoc.get('@context',{})
    logging.info("CONTEXT: {}".format(context))
    mapper = OboJsonMapper(digraph=digraph, context=context)
    ogs = obographdoc['graphs']
    base_og = ogs[0]
    for og in ogs:
        # TODO: refactor this
        mapper.add_obograph_digraph(og, xref_graph=xref_graph,
                                    logical_definitions=logical_definitions,
                                    property_chain_axioms=property_chain_axioms,
                                    reverse_edges=reverse_edges,
                                    **args)

    return {
        'id': base_og.get('id'),
        'meta': base_og.get('meta'),
        'graph': mapper.digraph,
        'xref_graph': xref_graph,
        'graphdoc': obographdoc,
        'logical_definitions': logical_definitions,
        'property_chain_axioms': property_chain_axioms
        }

def _get_association_nodes(digraph, sub, predicate, obj):
    """
    Given a subject, predicate, object, retrieve the OBAN association
    node from the evidence graph
    """
    subject_assocs = set()
    object_assocs = set()
    predicate_assocs = set()
    union_assoc = set()

    # A fast way of checking if the triple is reified is seeing if the
    # predicate exists as a node
    if predicate['pred'] in digraph.node:
        # association_has_subject
        for neighbor, edges in digraph.pred[sub].items():
            for edge in edges.values():
                if edge['pred'] == 'OBAN:association_has_subject':
                    subject_assocs.add(neighbor)

        # association_has_object
        for neighbor, edges in digraph.pred[obj].items():
            for edge in edges.values():
                if edge['pred'] == 'OBAN:association_has_object':
                    object_assocs.add(neighbor)

        # association_has_predicate
        for neighbor, edges in digraph.pred[predicate['pred']].items():
            for edge in edges.values():
                if edge['pred'] == 'OBAN:association_has_predicate':
                    predicate_assocs.add(neighbor)

        union_assoc = subject_assocs & object_assocs & predicate_assocs

    return union_assoc

def _triple_to_association(digraph, subject, predicate, obj):
    """
    Convert triple to association object
    """
    object_eq = []
    subject_eq = []
    if 'equivalentOriginalNodeTarget' in predicate:
        for eq in predicate['equivalentOriginalNodeTarget']:
            curies = contract_uri(eq, [get_curie_map()], shortest=True)
            if len(curies) != 0:
                object_eq.append(curies[0])

    if 'equivalentOriginalNodeSource' in predicate:
        for eq in predicate['equivalentOriginalNodeSource']:
            curies = contract_uri(eq, [get_curie_map()], shortest=True)
            if len(curies) != 0:
                subject_eq.append(curies[0])

    relation_lbl = predicate['lbl'][0] if predicate['lbl'] else None

    association = {
        'subject': {
            'id': subject,
            'label': digraph.node[subject]['lbl'],
            'iri': expand_uri(subject, [get_curie_map()])
        },
        'subject_eq': subject_eq,
        'relation': {
            'id': predicate['pred'],
            'label': relation_lbl,
            'iri': expand_uri(predicate['pred'], [get_curie_map()])
        },
        'object': {
            'id': obj,
            'label': digraph.node[obj]['lbl'],
            'iri': expand_uri(obj, [get_curie_map()])
        },
        'object_eq': object_eq,
        'provided_by': predicate['isDefinedBy'],
        'evidence_types': [],
        'publications': []
    }

    # get association node linked to ECO codes and publications
    association_nodes = _get_association_nodes(digraph, subject, predicate, obj)

    if len(list(association_nodes)) > 1:
        # This can happen with clique merging, for now log it
        # and combine both in association results
        logging.debug("Ambiguous association for %s, %s, %s",
                      subject, predicate, obj)

    for association_node in list(association_nodes):
        for obj, edges in digraph.adj[association_node].items():
            eco_codes = [eco['id'] for eco in association['evidence_types']]
            pubs = [pub['id'] for pub in association['publications']]

            for edge in edges.values():
                if edge['pred'] == 'RO:0002558' and obj not in eco_codes:
                    association['evidence_types'].append({
                        'id': obj,
                        'label': digraph.node[obj]['lbl']
                    })
                elif edge['pred'] == 'dc:source' and obj not in pubs:
                    association['publications'].append({
                        'id': obj,
                        'label': digraph.node[obj]['lbl']
                    })

    return association

def obograph_to_assoc_results(digraph):
    """
    Converts a multidigraph from convert_json_object
    to a list of association objects, which is easier
    to parse downstream for graph and table views

    :param: digraph: networkx.multidigraph or digraph
    :return: List, list of golr association objects

    """
    # Filter out oban edges initially, then retrieve using
    # _triple_to_association
    filter_edges = [
        'OBAN:association_has_subject',
        'OBAN:association_has_object',
        'OBAN:association_has_predicate',
        'dc:source',  # publications, web pages, etc
        'RO:0002558'  # ECO codes, etc
    ]

    association_results = []

    # Neighbors is a dict of adjacent nodes and outgoing edges
    for sub, neighbors in digraph.adjacency():
        # Iterate over nodes connected to outgoing edges (objects)
        for obj, edges_dict in neighbors.items():
            # Iterate over all edges connecting subject and object
            for edge_attr in edges_dict.values():
                if edge_attr['pred'] in filter_edges:
                    continue
                association_results.append(_triple_to_association(
                    digraph, sub, edge_attr, obj))

    return association_results
<commit_msg>cache evidence table<commit_after>"""
Mapping between obograph-JSON format and networkx
"""

from ontobio.ontol import LogicalDefinition, PropertyChainAxiom
from ontobio.vocabulary.relations import map_legacy_pred
from ontobio.util.scigraph_util import get_curie_map
from ontobio.golr.golr_associations import search_associations

import json
import networkx
import logging
from prefixcommons.curie_util import expand_uri, contract_uri
from diskcache import Cache
import tempfile

cache = Cache(tempfile.gettempdir())

class OboJsonMapper(object):
    def __init__(self,
                 digraph=None,
                 context=None):
        self.digraph = digraph
        self.context = context if context is not None else {}

    def add_obograph_digraph(
            self,
            og,
            node_type=None,
            predicates=None,
            xref_graph=None,
            logical_definitions=None,
            property_chain_axioms=None,
            parse_meta=True,
            reverse_edges=True,
            **args):
        """
        Converts a single obograph to Digraph edges and adds to an existing networkx DiGraph
        """
        digraph = self.digraph
        logging.info("NODES: {}".format(len(og['nodes'])))

        # if client passes an xref_graph we must parse metadata
        if xref_graph is not None:
            parse_meta = True

        for node in og['nodes']:
            is_obsolete = 'is_obsolete' in node and node['is_obsolete'] == 'true'
            if is_obsolete:
                continue
            if node_type is not None and ('type' not in node or node['type'] != node_type):
                continue
            id = self.contract_uri(node['id'])
            digraph.add_node(id, **node)
            if 'lbl' in node:
                digraph.node[id]['label'] = node['lbl']
            if parse_meta and 'meta' in node:
                if node['meta'] is None:
                    node['meta'] = {}
                meta = self.transform_meta(node['meta'])
                if xref_graph is not None and 'xrefs' in meta:
                    for x in meta['xrefs']:
                        xref_graph.add_edge(self.contract_uri(x['val']), id, source=id)
        logging.info("EDGES: {}".format(len(og['edges'])))
        for edge in og['edges']:
            sub = self.contract_uri(edge['sub'])
            obj = self.contract_uri(edge['obj'])
            pred = self.contract_uri(edge['pred'])
            pred = map_legacy_pred(pred)
            if pred == 'is_a':
                pred = 'subClassOf'
            if predicates is None or pred in predicates:
                meta = edge['meta'] if 'meta' in edge else {}
                if reverse_edges:
                    digraph.add_edge(obj, sub, pred=pred, **meta)
                else:
                    digraph.add_edge(sub, obj, pred=pred, **meta)

        if 'equivalentNodesSets' in og:
            nslist = og['equivalentNodesSets']
            logging.info("CLIQUES: {}".format(len(nslist)))
            for ns in nslist:
                equivNodeIds = ns['nodeIds']
                for i in ns['nodeIds']:
                    ix = self.contract_uri(i)
                    for j in ns['nodeIds']:
                        if i != j:
                            jx = self.contract_uri(j)
                            digraph.add_edge(ix, jx, pred='equivalentTo')
        if logical_definitions is not None and 'logicalDefinitionAxioms' in og:
            for a in og['logicalDefinitionAxioms']:
                ld = LogicalDefinition(self.contract_uri(a['definedClassId']),
                                       [self.contract_uri(x) for x in a['genusIds']],
                                       [(self.contract_uri(x['propertyId']),
                                         self.contract_uri(x['fillerId'])) for x in a['restrictions'] if x is not None])
                logical_definitions.append(ld)
        if property_chain_axioms is not None and 'propertyChainAxioms' in og:
            for a in og['propertyChainAxioms']:
                pca = PropertyChainAxiom(predicate_id=self.contract_uri(a['predicateId']),
                                         chain_predicate_ids=[self.contract_uri(x) for x in a['chainPredicateIds']])
                property_chain_axioms.append(pca)

    def transform_meta(self, meta):
        if 'basicPropertyValues' in meta:
            for x in meta['basicPropertyValues']:
                x['pred'] = self.contract_uri(x['pred'])
                x['val'] = self.contract_uri(x['val'])
        return meta

    def contract_uri(self, uri):
        if len(self.context.keys()) > 0:
            curies = contract_uri(uri, cmaps=[self.context])
            if len(curies) > 0:
                return sorted(curies, key=len)[0] # sort by length

        curies = sorted(contract_uri(uri), key=len) # Sort by length
        if len(curies) > 0:
            return curies[0]
        else:
            return uri

def convert_json_file(obographfile, **args):
    """
    Return a networkx MultiDiGraph of the ontologies
    serialized as a json string

    """
    file = open(obographfile, 'r')
    jsonstr = file.read()
    file.close()
    return convert_json_object(json.loads(jsonstr), **args)

def convert_json_object(obographdoc, reverse_edges=True, **args):
    """
    Return a networkx MultiDiGraph of the ontologies
    serialized as a json object

    """
    digraph = networkx.MultiDiGraph()
    xref_graph = networkx.MultiGraph()
    logical_definitions = []
    property_chain_axioms = []
    context = obographdoc.get('@context',{})
    logging.info("CONTEXT: {}".format(context))
    mapper = OboJsonMapper(digraph=digraph, context=context)
    ogs = obographdoc['graphs']
    base_og = ogs[0]
    for og in ogs:
        # TODO: refactor this
        mapper.add_obograph_digraph(og, xref_graph=xref_graph,
                                    logical_definitions=logical_definitions,
                                    property_chain_axioms=property_chain_axioms,
                                    reverse_edges=reverse_edges,
                                    **args)

    return {
        'id': base_og.get('id'),
        'meta': base_og.get('meta'),
        'graph': mapper.digraph,
        'xref_graph': xref_graph,
        'graphdoc': obographdoc,
        'logical_definitions': logical_definitions,
        'property_chain_axioms': property_chain_axioms
        }

def _get_association_nodes(digraph, sub, predicate, obj):
    """
    Given a subject, predicate, object, retrieve the OBAN association
    node from the evidence graph
    """
    subject_assocs = set()
    object_assocs = set()
    predicate_assocs = set()
    union_assoc = set()

    # A fast way of checking if the triple is reified is seeing if the
    # predicate exists as a node
    if predicate['pred'] in digraph.node:
        # association_has_subject
        for neighbor, edges in digraph.pred[sub].items():
            for edge in edges.values():
                if edge['pred'] == 'OBAN:association_has_subject':
                    subject_assocs.add(neighbor)

        # association_has_object
        for neighbor, edges in digraph.pred[obj].items():
            for edge in edges.values():
                if edge['pred'] == 'OBAN:association_has_object':
                    object_assocs.add(neighbor)

        # association_has_predicate
        for neighbor, edges in digraph.pred[predicate['pred']].items():
            for edge in edges.values():
                if edge['pred'] == 'OBAN:association_has_predicate':
                    predicate_assocs.add(neighbor)

        union_assoc = subject_assocs & object_assocs & predicate_assocs

    return union_assoc

def _triple_to_association(digraph, subject, predicate, obj):
    """
    Convert triple to association object
    """
    object_eq = []
    subject_eq = []
    if 'equivalentOriginalNodeTarget' in predicate:
        for eq in predicate['equivalentOriginalNodeTarget']:
            curies = contract_uri(eq, [get_curie_map()], shortest=True)
            if len(curies) != 0:
                object_eq.append(curies[0])

    if 'equivalentOriginalNodeSource' in predicate:
        for eq in predicate['equivalentOriginalNodeSource']:
            curies = contract_uri(eq, [get_curie_map()], shortest=True)
            if len(curies) != 0:
                subject_eq.append(curies[0])

    relation_lbl = predicate['lbl'][0] if predicate['lbl'] else None

    association = {
        'subject': {
            'id': subject,
            'label': digraph.node[subject]['lbl'],
            'iri': expand_uri(subject, [get_curie_map()])
        },
        'subject_eq': subject_eq,
        'relation': {
            'id': predicate['pred'],
            'label': relation_lbl,
            'iri': expand_uri(predicate['pred'], [get_curie_map()])
        },
        'object': {
            'id': obj,
            'label': digraph.node[obj]['lbl'],
            'iri': expand_uri(obj, [get_curie_map()])
        },
        'object_eq': object_eq,
        'provided_by': predicate['isDefinedBy'],
        'evidence_types': [],
        'publications': []
    }

    # get association node linked to ECO codes and publications
    association_nodes = _get_association_nodes(digraph, subject, predicate, obj)

    if len(list(association_nodes)) > 1:
        # This can happen with clique merging, for now log it
        # and combine both in association results
        logging.debug("Ambiguous association for %s, %s, %s",
                      subject, predicate, obj)

    for association_node in list(association_nodes):
        for obj, edges in digraph.adj[association_node].items():
            eco_codes = [eco['id'] for eco in association['evidence_types']]
            pubs = [pub['id'] for pub in association['publications']]

            for edge in edges.values():
                if edge['pred'] == 'RO:0002558' and obj not in eco_codes:
                    association['evidence_types'].append({
                        'id': obj,
                        'label': digraph.node[obj]['lbl']
                    })
                elif edge['pred'] == 'dc:source' and obj not in pubs:
                    association['publications'].append({
                        'id': obj,
                        'label': digraph.node[obj]['lbl']
                    })

    return association

def obograph_to_assoc_results(digraph):
    """
    Converts a multidigraph from convert_json_object
    to a list of association objects, which is easier
    to parse downstream for graph and table views

    :param: digraph: networkx.multidigraph or digraph
    :return: List, list of golr association objects

    """
    # Filter out oban edges initially, then retrieve using
    # _triple_to_association
    filter_edges = [
        'OBAN:association_has_subject',
        'OBAN:association_has_object',
        'OBAN:association_has_predicate',
        'dc:source',  # publications, web pages, etc
        'RO:0002558'  # ECO codes, etc
    ]

    association_results = []

    # Neighbors is a dict of adjacent nodes and outgoing edges
    for sub, neighbors in digraph.adjacency():
        # Iterate over nodes connected to outgoing edges (objects)
        for obj, edges_dict in neighbors.items():
            # Iterate over all edges connecting subject and object
            for edge_attr in edges_dict.values():
                if edge_attr['pred'] in filter_edges:
                    continue
                association_results.append(_triple_to_association(
                    digraph, sub, edge_attr, obj))

    return association_results

@cache.memoize()
def get_evidence_tables(id, user_agent):

    results = search_associations(
            fq={'id': id},
            facet=False,
            select_fields=['evidence_graph'],
            user_agent=user_agent)
    assoc_results = {}
    assoc = results['associations'][0] if len(results['associations']) > 0 else {}
    if assoc:
        eg = {'graphs': [assoc.get('evidence_graph')]}
        digraph = convert_json_object(eg, reverse_edges=False)['graph']
        assoc_results = {
            'associations': obograph_to_assoc_results(digraph),
            'numFound': len(obograph_to_assoc_results(digraph))
        }
    return assoc_results
<|endoftext|>


<commit_before>"""
Proctored Exams HTTP-based API endpoints
"""

import logging
import pytz
from datetime import datetime, timedelta

from django.utils.decorators import method_decorator

from rest_framework import status
from rest_framework.response import Response
from edx_proctoring.api import (
    create_exam,
    update_exam,
    get_exam_by_id,
    get_exam_by_content_id,
    start_exam_attempt,
    stop_exam_attempt,
    add_allowance_for_user,
    remove_allowance_for_user,
    get_active_exams_for_user,
    create_exam_attempt,
    get_allowances_for_course)
from edx_proctoring.exceptions import (
    ProctoredBaseException,
    ProctoredExamNotFoundException,
)
from edx_proctoring.serializers import ProctoredExamSerializer

from .utils import AuthenticatedAPIView

LOG = logging.getLogger("edx_proctoring_views")

def require_staff(func):
    """View decorator that requires that the user have staff permissions. """
    def wrapped(request, *args, **kwargs):  # pylint: disable=missing-docstring
        if request.user.is_staff:
            return func(request, *args, **kwargs)
        else:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={"detail": "Must be a Staff User to Perform this request."}
            )
    return wrapped

class ProctoredExamView(AuthenticatedAPIView):
    """
    Endpoint for the Proctored Exams
    /edx_proctoring/v1/proctored_exam/exam

    Supports:
        HTTP POST: Creates a new Exam.
        HTTP PUT: Updates an existing Exam.
        HTTP GET: Returns an existing exam (by id or by content id)

    HTTP POST
    Creates a new Exam.
    Expected POST data: {
        "course_id": "edX/DemoX/Demo_Course",
        "content_id": 123,
        "exam_name": "Midterm",
        "time_limit_mins": 90,
        "is_proctored": true,
        "external_id": "12213DASAD",
        "is_active": true
    }

    **POST data Parameters**
        * course_id: The unique identifier for the course.
        * content_id: This will be the pointer to the id of the piece of course_ware which is the proctored exam.
        * exam_name: This is the display name of the Exam (Midterm etc).
        * time_limit_mins: Time limit (in minutes) that a student can finish this exam.
        * is_proctored: Whether this exam actually is proctored or not.
        * external_id: This will be a integration specific ID - say to SoftwareSecure.
        * is_active: Whether this exam will be active.

    **Response Values**
        * {'exam_id': ##}, The exam_id of the created Proctored Exam.

    **Exceptions**
        * HTTP_400_BAD_REQUEST, data={"message": "Trying to create a duplicate exam."}

    HTTP PUT
    Updates an existing Exam.
    PUT data : {
        "exam_id": 533,
        "exam_name": "Final",
        "time_limit_mins": 120,
        "is_proctored": true,
        "external_id": 235
        "is_active": true
    }

    **PUT data Parameters**
        see the POST data parameters

    **Response Values**
        * {'exam_id': ##}, The exam_id of the created Proctored Exam.

    HTTP GET
        ** Scenarios **
        ?exam_id=533
        returns an existing exam  object matching the exam_id

        ?course_id=edX/DemoX/Demo_Course&content_id=123
        returns an existing exam object matching the course_id and the content_id
    """
    @method_decorator(require_staff)
    def post(self, request):
        """
        Http POST handler. Creates an exam.
        """
        serializer = ProctoredExamSerializer(data=request.DATA)
        if serializer.is_valid():
            exam_id = create_exam(
                course_id=request.DATA.get('course_id', None),
                content_id=request.DATA.get('content_id', None),
                exam_name=request.DATA.get('exam_name', None),
                time_limit_mins=request.DATA.get('time_limit_mins', None),
                is_proctored=request.DATA.get('is_proctored', None),
                external_id=request.DATA.get('external_id', None),
                is_active=request.DATA.get('is_active', None)
            )
            return Response({'exam_id': exam_id})
        else:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data=serializer.errors
            )

    @method_decorator(require_staff)
    def put(self, request):
        """
        HTTP PUT handler. To update an exam.
        calls the update_exam
        """
        try:
            exam_id = update_exam(
                exam_id=request.DATA.get('exam_id', None),
                exam_name=request.DATA.get('exam_name', None),
                time_limit_mins=request.DATA.get('time_limit_mins', None),
                is_proctored=request.DATA.get('is_proctored', None),
                external_id=request.DATA.get('external_id', None),
                is_active=request.DATA.get('is_active', None),
            )
            return Response({'exam_id': exam_id})
        except ProctoredExamNotFoundException:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"detail": "The exam_id does not exist."}
            )

    def get(self, request, exam_id=None, course_id=None, content_id=None):  # pylint: disable=unused-argument
        """
        HTTP GET handler.
            Scenarios:
                by exam_id: calls get_exam_by_id()
                by course_id, content_id: get_exam_by_content_id()

        """

        if exam_id:
            try:
                return Response(
                    data=get_exam_by_id(exam_id),
                    status=status.HTTP_200_OK
                )
            except ProctoredExamNotFoundException:
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={"detail": "The exam_id does not exist."}
                )
        else:
            # get by course_id & content_id
            try:
                return Response(
                    data=get_exam_by_content_id(course_id, content_id),
                    status=status.HTTP_200_OK
                )
            except ProctoredExamNotFoundException:
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={"detail": "The exam with course_id, content_id does not exist."}
                )

class StudentProctoredExamAttempt(AuthenticatedAPIView):
    """
    Endpoint for the StudentProctoredExamAttempt
    /edx_proctoring/v1/proctored_exam/attempt

    Supports:
        HTTP POST: Starts an exam attempt.
        HTTP PUT: Stops an exam attempt.
        HTTP GET: Returns the status of an exam attempt.

    HTTP POST
    create an exam attempt.
    Expected POST data: {
        "exam_id": "1",
        "external_id": "123",
        "start_clock": "True" or "true"
    }

    **POST data Parameters**
        * exam_id: The unique identifier for the course.
        * external_id: This will be a integration specific ID - say to SoftwareSecure.
        * start_clock: Whether to start the exam attempt immediately

    **Response Values**
        * {'exam_attempt_id': ##}, The exam_attempt_id of the created Proctored Exam Attempt.

    **Exceptions**
        * HTTP_400_BAD_REQUEST

    HTTP PUT
    Stops the existing exam attempt in progress
    PUT data : {
        "exam_id": 1
    }

    **PUT data Parameters**
        * exam_id: The unique identifier for the proctored exam attempt.

    **Response Values**
        * {'exam_attempt_id': ##}, The exam_attempt_id of the Proctored Exam Attempt..

    HTTP GET
        ** Scenarios **
        return the status of the exam attempt
    """

    def get(self, request):  # pylint: disable=unused-argument
        """
        HTTP GET Handler. Returns the status of the exam attempt.
        """

        exams = get_active_exams_for_user(request.user.id)

        if exams:
            exam = exams[0]

            # need to adjust for allowances
            expires_at = exam['attempt']['started_at'] + timedelta(minutes=exam['attempt']['allowed_time_limit_mins'])
            now_utc = datetime.now(pytz.UTC)

            if expires_at > now_utc:
                time_remaining_seconds = (expires_at - now_utc).seconds
            else:
                time_remaining_seconds = 0

            response_dict = {
                'in_timed_exam': True,
                'is_proctored': True,
                'exam_display_name': exam['exam']['exam_name'],
                'exam_url_path': '',
                'time_remaining_seconds': time_remaining_seconds,
                'low_threshold': 30,
                'critically_low_threshold': 15,
            }
        else:
            response_dict = {
                'in_timed_exam': False,
                'is_proctored': False,
            }

        return Response(
            data=response_dict,
            status=status.HTTP_200_OK
        )

    def post(self, request):
        """
        HTTP POST handler. To create an exam attempt.
        """
        start_immediately = request.DATA.get('start_clock', 'false').lower() == 'true'
        exam_id = request.DATA.get('exam_id', None)
        try:
            exam_attempt_id = create_exam_attempt(
                exam_id=exam_id,
                user_id=request.user.id,
                external_id=request.DATA.get('external_id', None),
            )

            if start_immediately:
                start_exam_attempt(exam_id, request.user.id)

            return Response({'exam_attempt_id': exam_attempt_id})

        except ProctoredBaseException, ex:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"detail": str(ex)}
            )

    def put(self, request):
        """
        HTTP POST handler. To stop an exam.
        """
        try:
            exam_attempt_id = stop_exam_attempt(
                exam_id=request.DATA.get('exam_id', None),
                user_id=request.user.id
            )
            return Response({"exam_attempt_id": exam_attempt_id})

        except ProctoredBaseException, ex:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"detail": str(ex)}
            )

class ExamAllowanceView(AuthenticatedAPIView):
    """
    Endpoint for the Exam Allowance
    /edx_proctoring/v1/proctored_exam/allowance

    Supports:
        HTTP PUT: Creates or Updates the allowance for a user.
        HTTP DELETE: Removed an allowance for a user.

    HTTP PUT
    Adds or updated the proctored exam allowance.
    PUT data : {
        "exam_id": 533,
        "user_id": 1,
        "key": 'extra_time',
        "value": '10'
    }

    **PUT data Parameters**
        * exam_id: The unique identifier for the course.
        * user_id: The unique identifier for the student.
        * key: key for the allowance entry
        * value: value for the allowance entry.

    **Response Values**
        * returns Nothing. Add or update the allowance for the user proctored exam.

    DELETE data : {
        "exam_id": 533,
        "user_id": 1,
        "key": 'extra_time'
    }
    **DELETE data Parameters**
        * exam_id: The unique identifier for the course.
        * user_id: The unique identifier for the student.
        * key: key for the user allowance.

    **Response Values**
        * returns Nothing. deletes the allowance for the user proctored exam.
    """
    def get(self, request, course_id):
        """
        HTTP GET handler. Get all allowances for a course.
        """
        return Response(get_allowances_for_course(
            course_id=course_id
        ))

    @method_decorator(require_staff)
    def put(self, request):
        """
        HTTP GET handler. Adds or updates Allowance
        """
        return Response(add_allowance_for_user(
            exam_id=request.DATA.get('exam_id', None),
            user_id=request.DATA.get('user_id', None),
            key=request.DATA.get('key', None),
            value=request.DATA.get('value', None)
        ))

    @method_decorator(require_staff)
    def delete(self, request):
        """
        HTTP DELETE handler. Removes Allowance.
        """
        return Response(remove_allowance_for_user(
            exam_id=request.DATA.get('exam_id', None),
            user_id=request.DATA.get('user_id', None),
            key=request.DATA.get('key', None)
        ))

class ActiveExamsForUserView(AuthenticatedAPIView):
    """
    Endpoint for the Active Exams for a user.
    /edx_proctoring/v1/proctored_exam/active_exams_for_user

    Supports:
        HTTP GET: returns a list of active exams for the user
    """
    def get(self, request):
        """
        returns the get_active_exams_for_user
        """
        return Response(get_active_exams_for_user(
            user_id=request.DATA.get('user_id', None),
            course_id=request.DATA.get('course_id', None)
        ))
<commit_msg>fix pylint<commit_after>"""
Proctored Exams HTTP-based API endpoints
"""

import logging
import pytz
from datetime import datetime, timedelta

from django.utils.decorators import method_decorator

from rest_framework import status
from rest_framework.response import Response
from edx_proctoring.api import (
    create_exam,
    update_exam,
    get_exam_by_id,
    get_exam_by_content_id,
    start_exam_attempt,
    stop_exam_attempt,
    add_allowance_for_user,
    remove_allowance_for_user,
    get_active_exams_for_user,
    create_exam_attempt,
    get_allowances_for_course)
from edx_proctoring.exceptions import (
    ProctoredBaseException,
    ProctoredExamNotFoundException,
)
from edx_proctoring.serializers import ProctoredExamSerializer

from .utils import AuthenticatedAPIView

LOG = logging.getLogger("edx_proctoring_views")

def require_staff(func):
    """View decorator that requires that the user have staff permissions. """
    def wrapped(request, *args, **kwargs):  # pylint: disable=missing-docstring
        if request.user.is_staff:
            return func(request, *args, **kwargs)
        else:
            return Response(
                status=status.HTTP_403_FORBIDDEN,
                data={"detail": "Must be a Staff User to Perform this request."}
            )
    return wrapped

class ProctoredExamView(AuthenticatedAPIView):
    """
    Endpoint for the Proctored Exams
    /edx_proctoring/v1/proctored_exam/exam

    Supports:
        HTTP POST: Creates a new Exam.
        HTTP PUT: Updates an existing Exam.
        HTTP GET: Returns an existing exam (by id or by content id)

    HTTP POST
    Creates a new Exam.
    Expected POST data: {
        "course_id": "edX/DemoX/Demo_Course",
        "content_id": 123,
        "exam_name": "Midterm",
        "time_limit_mins": 90,
        "is_proctored": true,
        "external_id": "12213DASAD",
        "is_active": true
    }

    **POST data Parameters**
        * course_id: The unique identifier for the course.
        * content_id: This will be the pointer to the id of the piece of course_ware which is the proctored exam.
        * exam_name: This is the display name of the Exam (Midterm etc).
        * time_limit_mins: Time limit (in minutes) that a student can finish this exam.
        * is_proctored: Whether this exam actually is proctored or not.
        * external_id: This will be a integration specific ID - say to SoftwareSecure.
        * is_active: Whether this exam will be active.

    **Response Values**
        * {'exam_id': ##}, The exam_id of the created Proctored Exam.

    **Exceptions**
        * HTTP_400_BAD_REQUEST, data={"message": "Trying to create a duplicate exam."}

    HTTP PUT
    Updates an existing Exam.
    PUT data : {
        "exam_id": 533,
        "exam_name": "Final",
        "time_limit_mins": 120,
        "is_proctored": true,
        "external_id": 235
        "is_active": true
    }

    **PUT data Parameters**
        see the POST data parameters

    **Response Values**
        * {'exam_id': ##}, The exam_id of the created Proctored Exam.

    HTTP GET
        ** Scenarios **
        ?exam_id=533
        returns an existing exam  object matching the exam_id

        ?course_id=edX/DemoX/Demo_Course&content_id=123
        returns an existing exam object matching the course_id and the content_id
    """
    @method_decorator(require_staff)
    def post(self, request):
        """
        Http POST handler. Creates an exam.
        """
        serializer = ProctoredExamSerializer(data=request.DATA)
        if serializer.is_valid():
            exam_id = create_exam(
                course_id=request.DATA.get('course_id', None),
                content_id=request.DATA.get('content_id', None),
                exam_name=request.DATA.get('exam_name', None),
                time_limit_mins=request.DATA.get('time_limit_mins', None),
                is_proctored=request.DATA.get('is_proctored', None),
                external_id=request.DATA.get('external_id', None),
                is_active=request.DATA.get('is_active', None)
            )
            return Response({'exam_id': exam_id})
        else:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data=serializer.errors
            )

    @method_decorator(require_staff)
    def put(self, request):
        """
        HTTP PUT handler. To update an exam.
        calls the update_exam
        """
        try:
            exam_id = update_exam(
                exam_id=request.DATA.get('exam_id', None),
                exam_name=request.DATA.get('exam_name', None),
                time_limit_mins=request.DATA.get('time_limit_mins', None),
                is_proctored=request.DATA.get('is_proctored', None),
                external_id=request.DATA.get('external_id', None),
                is_active=request.DATA.get('is_active', None),
            )
            return Response({'exam_id': exam_id})
        except ProctoredExamNotFoundException:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"detail": "The exam_id does not exist."}
            )

    def get(self, request, exam_id=None, course_id=None, content_id=None):  # pylint: disable=unused-argument
        """
        HTTP GET handler.
            Scenarios:
                by exam_id: calls get_exam_by_id()
                by course_id, content_id: get_exam_by_content_id()

        """

        if exam_id:
            try:
                return Response(
                    data=get_exam_by_id(exam_id),
                    status=status.HTTP_200_OK
                )
            except ProctoredExamNotFoundException:
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={"detail": "The exam_id does not exist."}
                )
        else:
            # get by course_id & content_id
            try:
                return Response(
                    data=get_exam_by_content_id(course_id, content_id),
                    status=status.HTTP_200_OK
                )
            except ProctoredExamNotFoundException:
                return Response(
                    status=status.HTTP_400_BAD_REQUEST,
                    data={"detail": "The exam with course_id, content_id does not exist."}
                )

class StudentProctoredExamAttempt(AuthenticatedAPIView):
    """
    Endpoint for the StudentProctoredExamAttempt
    /edx_proctoring/v1/proctored_exam/attempt

    Supports:
        HTTP POST: Starts an exam attempt.
        HTTP PUT: Stops an exam attempt.
        HTTP GET: Returns the status of an exam attempt.

    HTTP POST
    create an exam attempt.
    Expected POST data: {
        "exam_id": "1",
        "external_id": "123",
        "start_clock": "True" or "true"
    }

    **POST data Parameters**
        * exam_id: The unique identifier for the course.
        * external_id: This will be a integration specific ID - say to SoftwareSecure.
        * start_clock: Whether to start the exam attempt immediately

    **Response Values**
        * {'exam_attempt_id': ##}, The exam_attempt_id of the created Proctored Exam Attempt.

    **Exceptions**
        * HTTP_400_BAD_REQUEST

    HTTP PUT
    Stops the existing exam attempt in progress
    PUT data : {
        "exam_id": 1
    }

    **PUT data Parameters**
        * exam_id: The unique identifier for the proctored exam attempt.

    **Response Values**
        * {'exam_attempt_id': ##}, The exam_attempt_id of the Proctored Exam Attempt..

    HTTP GET
        ** Scenarios **
        return the status of the exam attempt
    """

    def get(self, request):  # pylint: disable=unused-argument
        """
        HTTP GET Handler. Returns the status of the exam attempt.
        """

        exams = get_active_exams_for_user(request.user.id)

        if exams:
            exam = exams[0]

            # need to adjust for allowances
            expires_at = exam['attempt']['started_at'] + timedelta(minutes=exam['attempt']['allowed_time_limit_mins'])
            now_utc = datetime.now(pytz.UTC)

            if expires_at > now_utc:
                time_remaining_seconds = (expires_at - now_utc).seconds
            else:
                time_remaining_seconds = 0

            response_dict = {
                'in_timed_exam': True,
                'is_proctored': True,
                'exam_display_name': exam['exam']['exam_name'],
                'exam_url_path': '',
                'time_remaining_seconds': time_remaining_seconds,
                'low_threshold': 30,
                'critically_low_threshold': 15,
            }
        else:
            response_dict = {
                'in_timed_exam': False,
                'is_proctored': False,
            }

        return Response(
            data=response_dict,
            status=status.HTTP_200_OK
        )

    def post(self, request):
        """
        HTTP POST handler. To create an exam attempt.
        """
        start_immediately = request.DATA.get('start_clock', 'false').lower() == 'true'
        exam_id = request.DATA.get('exam_id', None)
        try:
            exam_attempt_id = create_exam_attempt(
                exam_id=exam_id,
                user_id=request.user.id,
                external_id=request.DATA.get('external_id', None),
            )

            if start_immediately:
                start_exam_attempt(exam_id, request.user.id)

            return Response({'exam_attempt_id': exam_attempt_id})

        except ProctoredBaseException, ex:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"detail": str(ex)}
            )

    def put(self, request):
        """
        HTTP POST handler. To stop an exam.
        """
        try:
            exam_attempt_id = stop_exam_attempt(
                exam_id=request.DATA.get('exam_id', None),
                user_id=request.user.id
            )
            return Response({"exam_attempt_id": exam_attempt_id})

        except ProctoredBaseException, ex:
            return Response(
                status=status.HTTP_400_BAD_REQUEST,
                data={"detail": str(ex)}
            )

class ExamAllowanceView(AuthenticatedAPIView):
    """
    Endpoint for the Exam Allowance
    /edx_proctoring/v1/proctored_exam/allowance

    Supports:
        HTTP PUT: Creates or Updates the allowance for a user.
        HTTP DELETE: Removed an allowance for a user.

    HTTP PUT
    Adds or updated the proctored exam allowance.
    PUT data : {
        "exam_id": 533,
        "user_id": 1,
        "key": 'extra_time',
        "value": '10'
    }

    **PUT data Parameters**
        * exam_id: The unique identifier for the course.
        * user_id: The unique identifier for the student.
        * key: key for the allowance entry
        * value: value for the allowance entry.

    **Response Values**
        * returns Nothing. Add or update the allowance for the user proctored exam.

    DELETE data : {
        "exam_id": 533,
        "user_id": 1,
        "key": 'extra_time'
    }
    **DELETE data Parameters**
        * exam_id: The unique identifier for the course.
        * user_id: The unique identifier for the student.
        * key: key for the user allowance.

    **Response Values**
        * returns Nothing. deletes the allowance for the user proctored exam.
    """
    def get(self, request, course_id):  # pylint: disable=unused-argument
        """
        HTTP GET handler. Get all allowances for a course.
        """
        return Response(get_allowances_for_course(
            course_id=course_id
        ))

    @method_decorator(require_staff)
    def put(self, request):
        """
        HTTP GET handler. Adds or updates Allowance
        """
        return Response(add_allowance_for_user(
            exam_id=request.DATA.get('exam_id', None),
            user_id=request.DATA.get('user_id', None),
            key=request.DATA.get('key', None),
            value=request.DATA.get('value', None)
        ))

    @method_decorator(require_staff)
    def delete(self, request):
        """
        HTTP DELETE handler. Removes Allowance.
        """
        return Response(remove_allowance_for_user(
            exam_id=request.DATA.get('exam_id', None),
            user_id=request.DATA.get('user_id', None),
            key=request.DATA.get('key', None)
        ))

class ActiveExamsForUserView(AuthenticatedAPIView):
    """
    Endpoint for the Active Exams for a user.
    /edx_proctoring/v1/proctored_exam/active_exams_for_user

    Supports:
        HTTP GET: returns a list of active exams for the user
    """
    def get(self, request):
        """
        returns the get_active_exams_for_user
        """
        return Response(get_active_exams_for_user(
            user_id=request.DATA.get('user_id', None),
            course_id=request.DATA.get('course_id', None)
        ))
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
from waffle.models import Switch

def add_prereg_switch(*args, **kwargs):
    switch = Switch(name='osf_preregistration', active=False)
    switch.save()

def remove_prereg_switch(*args, **kwargs):
    Switch.objects.get(name='osf_preregistration').delete()

class Migration(migrations.Migration):
    dependencies = [
        ('osf', '0142_remove_waffle_analytics_flags'),
    ]
    operations = [
        migrations.RunPython(add_prereg_switch, remove_prereg_switch)
    ]
<commit_msg>add prereg schema with migration<commit_after># -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
from waffle.models import Switch

from osf.utils.migrations import ensure_schemas
from osf.features import OSF_PREREGISTRATION

def add_prereg_switch(*args, **kwargs):
    switch = Switch(name=OSF_PREREGISTRATION, active=False)
    switch.save()
    ensure_schemas()

def remove_prereg_switch(*args, **kwargs):
    Switch.objects.get(name=OSF_PREREGISTRATION).delete()
    ensure_schemas()

class Migration(migrations.Migration):
    dependencies = [
        ('osf', '0142_remove_waffle_analytics_flags'),
    ]
    operations = [
        migrations.RunPython(add_prereg_switch, remove_prereg_switch)
    ]
<|endoftext|>


<commit_before># halpmepl0x
# for those that messaged me with "i'm on dev, example.py dont work"
# thunderfox : can we put an example.py in 2.0 that just prints "you're a fucking retard"
# invisiblek wuz here
print("Please refer to documentation, there is no more example.py, only runserver.py")
fuck = "Would yous till like to massage the devs that example.py no work?"
if "needful" in fuck.lower():
    print("poon bang lure pls")
elif fuck.lower() != "no":
    print("you have been banned lol")
else:
    print("bless u")
<commit_msg>Update example.py (#438)<commit_after># halpmepl0x
# for those that messaged me with "i'm on dev, example.py dont work"
# thunderfox : can we put an example.py in 2.0 that just prints "you're a fucking retard"
# invisiblek wuz here
print("Please refer to documentation, there is no more example.py, only runserver.py")
fuck = "Would you still like to massage the devs that example.py no work?"
if "needful" in fuck.lower():
    print("poon bang lure pls")
elif fuck.lower() != "no":
    print("you have been banned lol")
else:
    print("bless u")
<|endoftext|>


<commit_before>#!/usr/bin/python
import os
import sys
import dircache

# First try to load the directory where the video files should be.
home = os.environ['HOME']
inst = home + '/.series'
directorytxt = inst + '/directory.txt'

# This can be called either by being the first time the program is runned or by using '-i'.
# That is why I define this function here.
def install():
    print 'Generating configuration file...'
    home = os.environ['HOME']
    inst = home + '/.series'
    programtxt = inst + '/program.txt'
    if not os.path.exists( inst ):
        os.mkdir( inst )
    programtxt = open( programtxt, 'w' )
    program = raw_input( 'What program should be used to view videos? (Give the name which you call the program with):' )
    programtxt.write( program.strip() )
    print 'Preference saved'
    return

if not os.path.exists(inst) or (len(sys.argv) > 1 and (sys.argv[1] == '-i' or sys.argv[1] == '--install')):
    if len(sys.argv) > 2:
        print 'Too many arguments. Use \'--help\' for help.'
        exit()
    install()
    exit()

if not os.path.exists(directorytxt):
    videosDirectory = raw_input( 'No directory with videos given. Type directory with the videos:' )
    directoryFile = open(directorytxt, 'w')
    directoryFile.write( os.path.abspath(videosDirectory))

directoryFile = open(directorytxt, 'r')
directory = directoryFile.readline().strip()
lastchaptertxt = directory + '/lastchapter.txt'

if len(sys.argv) > 1:
    if ( sys.argv[1] == '-r' or sys.argv[1] == '--restart' ) and os.path.exists( './lastchapter.txt' ):
        if len(sys.argv) > 2:
            print 'Too many arguments. Use \'--help\' for help.'
            exit()
        os.remove(lastchaptertxt)
    elif sys.argv[1] == '-i' or sys.argv[1] == '--install':
        if len(sys.argv) > 2:
            print 'Too many arguments. Use \'--help\' for help.'
            exit()
        install()
        exit()
    elif sys.argv[1] == '-s' or sys.argv[1] == '--set':
        if len(sys.argv) != 3:
            print 'Wrong use of \'-s\' or \'--set\'. Use \'--help\' to see correct use.'
        else:
            newFile =  open(lastchaptertxt, 'w' )
            try:
                chapter = int(sys.argv[2]) - 1
            except:
                print 'The Argument must be a number. Use \'--help\' for help.'
                exit()
            newFile.write( str(chapter) )
            newFile.close()
        exit()
    elif sys.argv[1] == '-b' or sys.argv[1] == '--back':
        if len(sys.argv) > 2:
            print 'Too many arguments. Use \'--help\' for help.'
        else:
            lastchapter = file( lastchaptertxt )
            chapter = lastchapter.readline()
            lastchapter.close()
            chapter = int(chapter)
            if  chapter > 0:
                newFile =  open(lastchaptertxt, 'w' )
                newFile.write( str(chapter - 1) )
                newFile.close()
        exit()
    elif sys.argv[1] == '-d' or sys.argv[1] == '--directory':
        if len(sys.argv) != 3:
            print 'Wrong use of \'' + sys.argv[1] + '\'. Use \'--help\' to see correct use.'
        elif not os.path.exists( sys.argv[2] ):
            print 'The given path(\'' + sys.argv[2] + '\') with the videos does not seem to exist. Aborting.'
        else:
            home = os.environ['HOME']
            inst = home + '/.series'
            directorytxt = inst + '/directory.txt'
            directoryFile = open(directorytxt, 'w')
            directoryFile.write( os.path.abspath(sys.argv[2]) )
        exit()
    elif sys.argv[1] == '-h' or sys.argv[1] == '--help':
        print 'This is the \'series\' program to view the chapters of a serie automatically in order v1.0'
        print 'Options:'
	print '-d --directory [path]    Sets the directory where the chapters to be played are.'
        print '-r --restart      \t Makes the program to start showing the chapters from the beginning again.'
        print '-i --install      \t (Re)Installs the program, i.e. lets you choose the program to view videos and creates \'$HOME/.series/program.txt\'.'
        print '-s --set [number] \t Sets the next chapter to be viewed to [number] and exits.'
        print '-b --back         \t Sets the next chapter to be viewed to the last viewed and exits.'
        print '-h --help         \t Prints this message.'
        exit()

print "Searching for Next Chapter..."

# Try to load the program that is to be used. First it looks for a 'program.txt' file
# in the folder where series was called, if not found is searches for '$HOME/.series/program.txt'.
# If the configuration file '$HOME/.series/program.txt'
# does not exist it asks the user what program he wants to use and creates this file.
if os.path.exists( './program.txt' ):
    program = file( './program.txt' )
else:
    program = os.environ['HOME'] + '/.series/program.txt'
    if not os.path.exists( program ):
        install()
    program = file( program )
program = program.readline().strip()

# If lastchapter.txt does not exist it means, that the program is called for the
# the first time in this folder. This means that the video files have to be lo-
# cated and written into lastchapter.txt
if not os.path.exists( directory + '/lastchapter.txt' ):
    newFile =  open(directory + '/lastchapter.txt', 'w' )
    newFile.write( '0' )
    newFile.close()

# Try to load a chapter. If no chapter seems to be left, say so to the user.
content = dircache.listdir(directory)

def suffix( extension ):
    def f(x): return x.endswith(extension)
    return f

extensions = ['.avi','.mpg','.mpeg','.ogg','.ogm','.mkv']

videos = []
for i in extensions:
    videos.extend( filter( suffix(i) , content ) )

#counter = 0;
#for i in videos:
#    videos[counter] = i.lower()
#    counter = counter + 1

videos = sorted(videos)

lastchapter = file( directory + '/lastchapter.txt' )
chapter = lastchapter.readline()

chapter = int(chapter)
if chapter < len(videos):
    newFile =  open(directory + '/lastchapter.txt', 'w' )
    newFile.write( str(chapter + 1) )
    newFile.close()

    print program + ' \"' + directory + '/' + videos[chapter] + '\"'
    os.system(program + ' \"' + directory + '/' + videos[chapter] + '\"')
else:
    print "No more chapters left :("
<commit_msg>Changed the way the interpreter is executed.<commit_after>#!/usr/bin/env python
import os
import sys
import dircache

# First try to load the directory where the video files should be.
home = os.environ['HOME']
inst = home + '/.series'
directorytxt = inst + '/directory.txt'

# This can be called either by being the first time the program is runned or by using '-i'.
# That is why I define this function here.
def install():
    print 'Generating configuration file...'
    home = os.environ['HOME']
    inst = home + '/.series'
    programtxt = inst + '/program.txt'
    if not os.path.exists( inst ):
        os.mkdir( inst )
    programtxt = open( programtxt, 'w' )
    program = raw_input( 'What program should be used to view videos? (Give the name which you call the program with):' )
    programtxt.write( program.strip() )
    print 'Preference saved'
    return

if not os.path.exists(inst) or (len(sys.argv) > 1 and (sys.argv[1] == '-i' or sys.argv[1] == '--install')):
    if len(sys.argv) > 2:
        print 'Too many arguments. Use \'--help\' for help.'
        exit()
    install()
    exit()

if not os.path.exists(directorytxt):
    videosDirectory = raw_input( 'No directory with videos given. Type directory with the videos:' )
    directoryFile = open(directorytxt, 'w')
    directoryFile.write( os.path.abspath(videosDirectory))

directoryFile = open(directorytxt, 'r')
directory = directoryFile.readline().strip()
lastchaptertxt = directory + '/lastchapter.txt'

if len(sys.argv) > 1:
    if ( sys.argv[1] == '-r' or sys.argv[1] == '--restart' ) and os.path.exists( './lastchapter.txt' ):
        if len(sys.argv) > 2:
            print 'Too many arguments. Use \'--help\' for help.'
            exit()
        os.remove(lastchaptertxt)
    elif sys.argv[1] == '-i' or sys.argv[1] == '--install':
        if len(sys.argv) > 2:
            print 'Too many arguments. Use \'--help\' for help.'
            exit()
        install()
        exit()
    elif sys.argv[1] == '-s' or sys.argv[1] == '--set':
        if len(sys.argv) != 3:
            print 'Wrong use of \'-s\' or \'--set\'. Use \'--help\' to see correct use.'
        else:
            newFile =  open(lastchaptertxt, 'w' )
            try:
                chapter = int(sys.argv[2]) - 1
            except:
                print 'The Argument must be a number. Use \'--help\' for help.'
                exit()
            newFile.write( str(chapter) )
            newFile.close()
        exit()
    elif sys.argv[1] == '-b' or sys.argv[1] == '--back':
        if len(sys.argv) > 2:
            print 'Too many arguments. Use \'--help\' for help.'
        else:
            lastchapter = file( lastchaptertxt )
            chapter = lastchapter.readline()
            lastchapter.close()
            chapter = int(chapter)
            if  chapter > 0:
                newFile =  open(lastchaptertxt, 'w' )
                newFile.write( str(chapter - 1) )
                newFile.close()
        exit()
    elif sys.argv[1] == '-d' or sys.argv[1] == '--directory':
        if len(sys.argv) != 3:
            print 'Wrong use of \'' + sys.argv[1] + '\'. Use \'--help\' to see correct use.'
        elif not os.path.exists( sys.argv[2] ):
            print 'The given path(\'' + sys.argv[2] + '\') with the videos does not seem to exist. Aborting.'
        else:
            home = os.environ['HOME']
            inst = home + '/.series'
            directorytxt = inst + '/directory.txt'
            directoryFile = open(directorytxt, 'w')
            directoryFile.write( os.path.abspath(sys.argv[2]) )
        exit()
    elif sys.argv[1] == '-h' or sys.argv[1] == '--help':
        print 'This is the \'series\' program to view the chapters of a serie automatically in order v1.0'
        print 'Options:'
	print '-d --directory [path]    Sets the directory where the chapters to be played are.'
        print '-r --restart      \t Makes the program to start showing the chapters from the beginning again.'
        print '-i --install      \t (Re)Installs the program, i.e. lets you choose the program to view videos and creates \'$HOME/.series/program.txt\'.'
        print '-s --set [number] \t Sets the next chapter to be viewed to [number] and exits.'
        print '-b --back         \t Sets the next chapter to be viewed to the last viewed and exits.'
        print '-h --help         \t Prints this message.'
        exit()

print "Searching for Next Chapter..."

# Try to load the program that is to be used. First it looks for a 'program.txt' file
# in the folder where series was called, if not found is searches for '$HOME/.series/program.txt'.
# If the configuration file '$HOME/.series/program.txt'
# does not exist it asks the user what program he wants to use and creates this file.
if os.path.exists( './program.txt' ):
    program = file( './program.txt' )
else:
    program = os.environ['HOME'] + '/.series/program.txt'
    if not os.path.exists( program ):
        install()
    program = file( program )
program = program.readline().strip()

# If lastchapter.txt does not exist it means, that the program is called for the
# the first time in this folder. This means that the video files have to be lo-
# cated and written into lastchapter.txt
if not os.path.exists( directory + '/lastchapter.txt' ):
    newFile =  open(directory + '/lastchapter.txt', 'w' )
    newFile.write( '0' )
    newFile.close()

# Try to load a chapter. If no chapter seems to be left, say so to the user.
content = dircache.listdir(directory)

def suffix( extension ):
    def f(x): return x.endswith(extension)
    return f

extensions = ['.avi','.mpg','.mpeg','.ogg','.ogm','.mkv']

videos = []
for i in extensions:
    videos.extend( filter( suffix(i) , content ) )

#counter = 0;
#for i in videos:
#    videos[counter] = i.lower()
#    counter = counter + 1

videos = sorted(videos)

lastchapter = file( directory + '/lastchapter.txt' )
chapter = lastchapter.readline()

chapter = int(chapter)
if chapter < len(videos):
    newFile =  open(directory + '/lastchapter.txt', 'w' )
    newFile.write( str(chapter + 1) )
    newFile.close()

    print program + ' \"' + directory + '/' + videos[chapter] + '\"'
    os.system(program + ' \"' + directory + '/' + videos[chapter] + '\"')
else:
    print "No more chapters left :("
<|endoftext|>