General guidelines · You may complete this assignment individually or with a partner. · You must not change the starter code other than by filling in missing method and class bodies as described in...

1 answer below »

General guidelines


· You may complete this assignment individually or with a partner.


· You must not change the starter code other than by filling in missing method and class bodies as described in your tasks below, or uncommenting the parts we tell you to (marked with "TODO:" comments in the starter code).
It's always okay to add helper methods, however.


· The tasks are not designed to be equally difficult, or even in increasing order of difficulty. They are just laid out in logical order.


· Although implementing a complex application can be challenging at first, we will guide you through a progression of tasks, in order to gradually build pieces of your implementation. However, it is your responsibility to read through this handout and the starter code we provide,carefully, and to understand how the classes work together in the context of the application.


Learning Goals


By the end of this assignment you should be able to:


· read code you didn't write and understand its design and implementation, including:


o reading the class and method docstrings carefully (including attributes, representation invariants, preconditions, etc.)


o determining relationships between classes, by applying your knowledge of composition and inheritance


· complete a partial implementation of a class, including:


o reading the representation invariants to enforce important facts about implementation decisions


o reading the preconditions to factor in assumptions that they permit


o writing the required methods


· implement a class from a provided specification, including:


o defining instance attributes and methods


o writing the class documentation and method docstrings


o implementing the class functionality according to specs


o using inheritance: defining a subclass of another class


Introduction


MewbileTech (Logo:) is a new phone company who wants to get a foothold on the Toronto mobile phone market. They are asking for your help in building software to keep track of their historic customer data. They want the program to read in a dataset of customer calls, set geographically in Toronto, and produce a visualization of this past calling activity on a real map of the city. The MewbileTech operator should then be able to filter results, (for example, displaying only calls and texts from a particular customer), or display a detailed bill that a given customer would have been sent during a specific month. We use the term "to filter" to indicate that we want to display only those calls which match the search criterion.


Please note that this is not a "real-time" system where calls would be recorded as they are made, but rather a system to visualize calls that have already been made. It is intended for use by a MewbileTech operator to analyze customers' calling patterns.


The classes you will implement include some methods that are never used in the present application, such as a method for cancelling a contract. These provide services that MewbileTech anticipates will be used in future applications.


Phone Company Specifications


A MewbileTechcustomerhas a unique id number, and one or more phone lines. Eachphone linehas a unique phone number associated with it (which cannot change once assigned to this phone line), a contract, the call history associated with this phone line, and the monthly bill for every month when the line was in use under the current contract. A phone line can change contracts over time, but the application you are writing does not offer that operation to the MewbileTech operator.


Acallhas a source number, destination number, the time when the call was made, the duration of the call, and the geographic locations (as a pair of longitude+latitude coordinates) of both the source of the call and the destination of the call.


Thecall historyof each customer is a record of all outgoing and incoming calls.


Acontractis an agreement between MewbileTech and a given customer. Contracts come in three types:Month-to-Month Contract,Term Contract, andPrepaid Contract. The type of contract determines things like whether the customer must commit to a term (i.e., a period of time), the rate charged per minute of voice time, and the number of included (free) minutes per month. Details on each contract type are provided in Task 3.


A customer can have each of their phone lines under a different type of contract, but each line is associated with exactly one contract.


As any respectable company, MewbileTech must be able to bill its customers correctly. A monthlybillhas a number of billed minutes, as well as the free minutes included in the customer's contract (if any) that have been used in that monthly billing cycle.


Note: Although, as you will see, the application can display the bill for any customer in any month, it does not have any capabilities related to payments. Each bill is generated assuming there was no unpaid carry-over from the previous month.


Warmup Task A: Get familiar with the input data format


Download theassignment materialsand unzip it into yourassignments/a1/folder.


The customer data, including calls, are stored in a file that is in a format called JSON. JSON format stores data as key-value pairs, similar to dictionaries in python. If you're curious to explore this format in depth, we recommend this introduction to theJSON format. However, we will give you all the information you need about to how to interpret the structure of the data you will be working with, and a sample dataset in a file calleddataset.json. This dataset is not real; it was automatically generated. Open it up and review its structure and content. To help you understand the structure, we are also providing a smaller dataset calleddata.pywhich is formatted to make the structure of the data stand out. Here is some information that will help you find your way around:


· The data contains two keys,eventsandcustomers.


· The value for theeventskey is a list of call and SMS events (aka text messages) for all customers.NOTE:We will ignore the SMS events in this assignment. You are welcome to think of how you would extend the MewbileTech application to support SMS events too, but this is out of the scope of this assignment.


· Each event in the list has several key-value pairs, representing the type of event, source and destination number, time when the event occurred, location (longitude/latitude coordinates), etc.


· The value for thecustomerskey is a list of customers.


· Each customer in the list has two key-value pairs: the keylinesis associated with a list of phone lines, and the keyidis associated with the id of the customer who owns those lines.


Warmup Task B: Starter code, setup, and initial run


Task B.1: Run the starter code


Before you go into the starter code, you should know that aside from the python code, sample tests, and the sample dataset, we provide you with adiagram (in the file
StarterCodeArchitecture.pdf
)to help you visualize some of the key classes. You may have a peek over this diagram and think about relationships between the classes, but don't worry if you don't understand it all in one go. Remember to revisit this diagram as you follow our instructions in the next tasks.


We'll dig in to the code shortly. But first, do the following to make sure your computer is set up properly to run the program.


1. Open the fileapplication.pyin PyCharm and skim through it. This module contains the main program which drives the entire application. Don't worry if you don't understand everything, we will guide you through this at the right time.


2. Run that file. You will see a new window open up showing a blank map of Toronto. This is all the program does right now, but you are about to change that! You can close the window by pressing the X in the corner, as you would any other window.



Important notes for Mac users: While your code must run on the lab machines, you are welcome to use your own computer to work on the assignment as well. However, for those of you running this assignment on Mac computers, the latest OSX (Mojave, 10.14) has a bug where it may not display the images for the map and will instead render a blank screen when running. A workaround has been found to work for most users, which requires the installation of the most recent version of Python 3.7.2, which you can download as a binary installation package fromhere. Only the macOS 64-bit/32-bit installer has been tested and proven to work with the assignment. With this installation the assignment will show the map and respond to user input events (we'll discuss user input events later, when we discuss filtering).


There is another small issue that is only encountered in OSX (including in older versions this time). After applying a filter to the data, if a keyboard button is pressed without first clicking on the pygame window again, the program will crash. In order to avoid that from happening, use the following steps after launching the code:


1. Use the keyboard to apply a filter or output the billing details for a customer


2. Enter the data into the popup window(s)


3. Click the "Apply Filter" button(s)


4. Click on the map


If step 4 is skipped and another button is pressed, the program will crash.


Task B.2: Understand how data is loaded


Important: you may not modify anything belowif __name__ == '__main__':inapplication.py(or any of the other modules).


This main program uses a function calledimport_data(), that we have given you, to import data from a json file. This function uses Python'sjsonmodule to read and parse json data. If you are curious to learn how the json module'sload()function works, you can familiarize yourselves with ithere.


import_data()takes an open file and stores all its contents in a dictionary, which is then returned. This dictionary contains thecustomerskey and theeventskey, as loaded from the input file format you learned about in Task A. Make sure that you understand the structure of the dictionary returned by this function; in the coming tasks, you will need to pull information out of this dictionary.


Next, have a look over the providedcreate_customers()function inapplication.py. This function's role is to create instances of the Customer class, using the data loaded from the json file.


This function goes through the loaded data (passed in through thelogargument) and instantiates Customer objects, then return them in a list. In thelogdictionary, the value corresponding to thecustomerskey contains a list of customers, where each customer is stored as a dictionary itself, with the following keys:


· key 'id' corresponds to a value representing the customer id


· key 'lines' corresponds to a list of phone lines for this customer. Each phone line in turn is a list of dictionaries, with the following keys:


o number: the phone number associated with the phone line


o contract: the contract type associated with the phone line


You will implement contracts later, so for now, just leave commented out the lines of code where contracts are instantiated, as mentioned in the "TODO" comments. After you have completed Task 3.4, you will come back and uncomment these lines of code.


Task 1: Reading and recording call events


You are now ready to begin writing code!A note about doctests: we've omitted doctests from most of the starter code, and you aren't required to write doctests for this assignment. You will write your tests in pytest instead. This is because, in this assignment, initializing objects for testing is more involved than usual, and would make for messy docstrings. However, you will be required to follow all other aspects of the class design recipe.


Your first code-writing task is to implement theprocess_event_history()function in theapplicationmodule. The role of this function is to instantiateCallobjects and store each of them in the right customer's records.


The parameterlogis a dictionary containing all calls and SMSs from the dataset. Its keyeventscontains a list of call and SMS events, which you will go through, creatingCallobjects out of events of type "call", and ignoring events of type "sms".


Each event you extract from the log dictionary is itself a dictionary. A call event has the following keys:


· 'type' corresponds to the type of event ("call"or"sms")


· 'src_number' corresponds to the caller's phone number


· 'dst_number' corresponds to the callee's phone number


· 'time' corresponds to the time when this call was made (for example:"2018-01-02 01:07:10")


· 'duration' corresponds to the call duration (in seconds)


· 'src_loc' corresponds to the caller's location (longitude+latitude)


· 'dst_loc' corresponds to the callee's location (longitude+latitude)


We have provided the first three lines of code in this method, to show you how to extract data from the dictionary and to give you an example of using thedatetimemodule. You will need to familiarize yourselves with this module by reading thedatetime documentation


As you go through the list of events and instantiateCallobjects, you must also record these in the right Customer's records. This must be done by using theCustomerclass API, so please consult it to find the methods for registering incoming or outgoing calls. You will implement some of these Customer methods in the next task so calling them now will have no effect, but put the calls in. As we discussed in lectures, in order to use an API, all you need to know is the interface, not the implementation.


Additionally, as you instantiate new events, every time a new month is detected for the current event you are processing (whether it's a call or SMS!), you must advance all customers to a new month of contract. This is done using thenew_month()function from theapplication.pymodule.


To help you understand this better, we define a few concepts which we will use in this assignment:


1.
Advancing to a new monthis the action of moving to a new month of contract, which must be done for the purposes of billing. Thenew_month()function in this module advances the month for every customer for each of their phonelines, according to the respective contract types (remember that customers can have multiple phone lines each, and that each phone line can be under a different type of contract).


2.
Gap monthis defined as a month with no activity for any customer (no calls or SMSs), despite there being activity in the month prior to the gap month, as well as in the month after the gap month.


We are making several simplifying assumptions, and enforcing them via preconditions in the code:


· The input data is guaranteed to be sorted chronologically. Therefore, advancing to a new month of contract can be safely done. For example, once you encounter an event from February 2019, you are guaranteed the next events you process cannot be from a month before February 2019.


· There is no gap month in the input data. This implies that for the timespan between the first event and the last event in the dataset, there is no month with zero activity from all customers. In other words, while one customer could have zero activity during a specific month X, there is at least one other customer who had some activity during that month. Therefore, according to the definition of advancing a month, customers with zero activity for a specific month will still get a Bill instance created. This bill will just have whatever baseline cost the corresponding contract specifies (more on this when we get to contracts in a later task).



Important: We are providing you with afind_customer_by_number()function. You must use it to figure out which customer a number belongs to. (Our autotesting depends on this, and you will lose marks if you don't.)



Check your work: Write pytest tests to make sure your code so far works according to specifications. It is your responsibility to write good tests!


Task 2: Really recording call events


Your code from Task 1 has called some methods that are not yet implemented. In this task, you'll write those methods, so that call information will be recorded inside objects created by the program.


Task 2.1: Complete classCallHistory


Opencall_history.pyand read the docstrings for theCallHistoryclass and its methods. We have already partially implemented this class and designed some of its methods for you; donotmodify the attributes or initializers here. Your job is to complete the following methods, according to the specifications we've given you in the code:


· register_outgoing_call


· register_incoming_call


Task 2.2: Complete classPhoneLine


Next, openphoneline.pyand read the documentation we've provided for thePhoneLineclass. You will have to review the starter code we provided for customer billing inbill.pyas well as theContractclass incontract.py. You will see thatContractis an abstract class representing a generic contract; we will add specific contract types in a later task.


After you understand the connections between these three classes, your task is to implement the following methods in thePhoneLineclass, according to the specifications from the docstrings:


· make_call


· receive_call


Task 2.3: Complete classCustomer


Finally, opencustomer.pyand read the docstrings for theCustomerclass and its methods. We have already partially implemented this class, so make surenotto modify the attributes or initializers here. You must complete the following methods though, according to the specifications we've given you in the code:


· make_call


· receive_call



Hint:make sure to add the calls to the correct phone line of the customer. You should be familiar at this point with the PhoneLine API.



Visualize your work: run the application inapplication.pyand make sure that the calls are now being displayed. At the start the visualization displays all the Calls (round phone images), scattered on the map, with lines indicating the connection between the source and destination of that Call!



Check your work: Write pytest tests to make sure your code so far works according to specifications. It is your responsibility to write good tests!


Task 3: Contracts


As we saw earlier, the starter code includes aContractclass incontract.py, which contains a basic set of attributes and methods. Each contract contains at the very least a start date and a Bill object.


We are making some simplifying assumptions:


· regardless of the contract type, the billing cycle starts on the 1st of the month and ends on the last day of the month.


· incoming calls are free.


· for any given contract type, the rate charged for a minute is always the same. In other words, all contracts of the same type charge the same rate per minute, but different contract types may charge different rates.


The interaction between contracts and bills is something you have to piece together carefully, so we will give you some guidance to see the "big picture", as follows:


· whenever a new month is encountered in the historic data from the input dataset, we "advance" to a new month of contract all customers and all phone lines. You did this already in application.py, by calling thenew_month()function from that module in theprocess_event_history(), whenever the timestamp of an event indicates a new month is encountered.


· given that historic records get loaded gradually, thebillattribute of a contract always represents the "latest" bill (the one for the month we are currently loading events for). Once the data is fully processed, thebillis basically corresponds to the last month encountered in the input data.


· notice that the contract instance has the information on how much a call should be charged, whether a call should be free or billed, etc. Therefore, you must ensure that each monthly bill can get this information, whenever you advance the month.


To understand the interaction between contracts and bills, ask yourself these questions:


· is this the first month of the contract?


· do you get new free minutes for the contract type?


· how do you handle billing a call?


· what is specific to each contract type?



Your task: implement classes for each of the three types of customer contracts: month-to-month, term, and prepaid. The new classes must be calledMTMContract,TermContract, andPrepaidContractrespectively.


To prepare for this task, you need to do two things:


· First, review all the variables we have provided in modulecontractthat store predefined rates and fees.You must not modify these.


· Then, make sure you understand theBillclass, defined in modulebill. Pay particular attention to methodsset_rates()andadd_fixed_cost(). They will be important when you need to create a new monthly bill.


Task 3.1: Implement term contracts


Aterm contractis a type of Contract with a specific start date and end date, and which requires a commitment until the end date. A term contract comes with an initial largeterm depositadded to the bill of the first month of the contract. If the customer cancels the contract early, the deposit is forfeited. If the contract is carried to term, the customer gets back the term deposit. That is, if the contract gets cancelledafterthe end date of the contract, then the term deposit is returned to the customer, minus that month's cost.


The perks of the term contract consist of:


· having a lower monthly cost than a month-to-month contract


· lower calling rates, and


· a number of free minutes included each month, which refresh when a new month starts. Free minutes are used up first, so the customer only gets billed for minutes of voice time once the freebies have been used up.


To prepare, have a look over theTERM_DEPOSITfee defined incontract.py, as well as the corresponding rate per minute of voice time.


You can assume that the bill is paid on time each month by the customer, so you don't have to worry about carrying over the previous month's bill for a Term Contract. It is important to note that a customer in a Term Contract can continue with the same contract past its end date (at the same rate and conditions), and can do so until the contract is explicitly cancelled. When the term is over (and the contract hasn't been cancelled), instead of adding a new term deposit and refunding the old one, the deposit simply gets carried over and does not get refunded until the contract gets cancelled.



Your task:Implement classTermContractas a subclass ofContract. TheTermContractmethods must follow the same interface as theContractclass. Consider each aspect of a term contract carefully!


Task 3.2: Implement month-to-month contracts


Themonth-to-month contractis a Contract with no end date and no initial term deposit. This type of contract has higher rates for calls than a term contract, and comes with no free minutes included, but also involves no term commitment. Have a look atcontract.pyfor all the rates per minute for each type of contract.


Just like for a Term Contract, you can assume that the bill is paid on time each month by the customer, so you don't have to worry about carrying over an unpaid bill.



Your task:Implement theMTMContractclass as a subclass ofContract. TheMTMContractclass methods must follow the same interface as theContractclass. Consider each aspect of this contract carefully!


Task 3.3: Implement prepaid contracts


Aprepaid contracthas a start date but does not have an end date, and it comes with no included minutes. It has an associatedbalance, which is the amount of money the customer owes. If the balance is negative, this indicates that the customer has this much credit, that is, has prepaid this much. The customer must prepay some amount when signing up for the prepaid contract, but it can be any amount.


When a new month is started, the balance from the previous month gets carried over into the new month, and if there is less than a $10 credit left in the account, the balance must get a top-up of $25 in credit (again, keep in mind that a negative amount indicates a credit). When cancelling the contract, if the contract still has left some credit on it (a negative balance), then the amount left is forfeited. If the balance is positive, this should be returned by thecancel_contract()method, so that an application can notify the customer about a remaining balance to be paid (although our application does not do this).



Your task:Implement thePrepaidContractclass as a subclass ofContract. ThePrepaidContractclass methods must follow the same interface as theContractclass. Consider each aspect of this contract carefully!


Task 3.4: Give customers the right type of contract for each phone line


Now go back and update the code in thecreate_customers()function in the main application module, by uncommenting the piece of code which instantiates contracts.


As you might notice, a dataset does not contain the start date (or end date, where applicable) for a contract, nor does it specify an initial balance for a PrepaidContract. In the code we provided you, we use the following (arbitrarily chosen) values:


· all MTMContracts start on the 25th of December 2017.


· all TermContracts start on the 25th of December 2017 and end of the 25th of June 2019.


· all PrepaidContracts start on the 25th of December 2017.



Note:Changing contracts is not supported by the user interface of the application, however, you might be wondering how some other application could support it. We are making the simplifying assumption that in order to change contracts, a customer must first cancel the corresponding phone line at the end of the contract, and then an operator can help the customer create a new phone line with the same number but under a new contract.



Check your work: Write pytest tests to make sure your code so far works according to specifications. It is your responsibility to write good tests!


Task 4: Filtering events and displaying bills


Now that you have the data loaded and recorded in the system, as well as contracts implemented, we are ready to add the filtering feature, which will allow the user to filter the calls that are displayed, for example so that they see only calls from a certain customer.


First, let's understand the various filters and what they will do. If you run the application, the menu bar in the visualization window informs the application user of the supported types of actions. The filtering actions are invoked by pressing the following keys:


· c: show only calls (to and from) phone lines belonging to a givencustomer id


· d: show only calls (made or received) with at least (or at most) a givenduration


· l: show only calls (to and from) a givenlocation


· r: reset all filters applied so far


Once the corresponding key is pressed, filters that require additional information ask the user to enter it. For example, the 'c' filter asks the user to enter the desired customer id.



Note:The first time you press a key to apply a filter, you will see a window pop-up with a welcome message from the MewbileTech management system. This window will appear on top of your filter pop-up, so you must close the welcome pop-upbeforeyou can proceed to applying a filter. Why does this happen? This pop-up window was intended to be displayed when you first launch the application, rather than when you press a filter key. However, the MewbileTech cat decided to meddle with the application functionality and we're stuck with this behaviour, which you can safely ignore. :)


The user can apply one filter after another. The visualization starts by showing all the calls in the system, for all customers and all their phone lines, and then each filter gradually narrows down the search by only querying the remaining records left after applying the previous filter. For example, you can apply a 'c' filter, followed by an 'l' filter, and then an 'd' filter, in order to determine all the calls made or received by a certain customer, in a given location, with a duration of over or under a number of seconds. The 'r' switch resets all the filters applied so far, and restarts the search on the entire dataset.


This is what the codewilldo.Youneed to implement all of the incomplete filter classes. Let's make it so!



Note:If you are curious to know how the actual visualization process works, you are welcome to look intovisualizer.py, but you do not need to understand the visualizer module in order to solve this assignment. However, if you are curious to explore thevisualizer.pymodule, we recommend that you read the docstring of therender_drawables()method of theVisualizerclass. The main block in theapplicationmodule sends it a list ofDrawables, which it then draws them on the pygame window. You might wish to also look at thehandle_window_events()method, which takes input from the application user and acts accordingly. You will notice a whole bunch of filter objects, of various types, for example,DurationFilter,LocationFilter,CustomerFilter, etc.


Task 4.1: Implement the filters


In modulefilter, review theFilterclass and its documentation carefully. The actual filtering work is done in theapply()method, which acts accordingly for each type of filter. Your task is to implement all the apply() methods in the filter classes which subclass Filter.


Each of the filters has its own definition of what is a valid user input. This is displayed to the user in the application as a visual prompt (the__str__()method returns the visual prompt message). Nevertheless, a user might still enter incorrectly formatted input, or may enter invalid input (e.g., a customer id which does not exist). For each filter, you must enforce "sanity" checks in theapply()method to ensure that your code will not crash if given an incorrect or invalid input. We want you to consider how malformed or invalid inputs (as given in thefilter_stringargument) can impact your implementation of the apply() method, and to make your code robust. We are not explicitly telling you what cases to consider because we want to encourage you to consider robustness during your implementation. Ultimately, if during execution of your program a cat walks across your keyboard (don't they love to?) your code should not crash and it should do the right thing.


There are two ways to check for incorrect inputs: - ensure that an incorrect input cannot crash your code by using explicit validity checks (e.g., using if statements), or - use try/except blocks to catch whichever Exception may be raised by your code due to an incorrect or invalid input. For the latter, revisit the lecture prep on Exceptions for an example of how to handle exceptions.



Visualize your work: run theapplication.pyand try applying each filter. As you apply more and more filters, some calls will disappear from the map. Keep in mind that although visualization is nice for seeing your code at work, it is not a thorough way to test your code! It is your responsibility to thoroughly test your code by making sure you write pytest test cases for a variety of corner cases. And don't forget the cat test. (In the absence of a cat, your elbow will do.) No input should make the code crash.



Check your work: Write pytest tests to make sure your code so far works according to specifications. It is your responsibility to write good tests!


"Task" 4.2: Display bills


There is one more user action we have not discussed yet: "m" is used to display the monthly bill that a customer would have received for a given month. There is no code for you to write. We have provided you with the code for this operation. Review it, and try displaying bills in the visualization window.


Task 5: Speeding up execution


This task is optional, but it covers really cool concepts, so we strongly recommend that you do it. We are foreshadowing some concepts you will learn in later courses, but about which it is useful to form an intuition early on.


You will see later on in this course how the choice of data structures and algorithms can make a big difference in your program's efficiency. Another way of speeding up your program is to use multiple resources in parallel.


Let's consider your computer's main processing resource: the central processing unit (CPU). Your computer's CPU has multiple processors or "cores," each capable of computations independent of the other(s). Most programs you have written so far run entirely sequentially -- one instruction after another -- until they are finished. However, what if we could find pieces of your program which can be run completely independently? If so, that would enable your CPU to run those pieces at the same time, but on different cores. As a result, execution should be much faster. For example, if your code processes 4 million data items in 40 seconds, and we break down this computation into 4 equal pieces of data, each with 1 million data items, and process all of them in parallel, then the entire computation should only take 10 seconds. In reality we might not get this exact speed-up, because it depends on the specific computation and many other aspects of computing in parallel. When you learn the intricacies of parallelism in later courses, you will be able to analyze the performance of your program under a variety of factors, but we want to give you a flavour of what parallel computing means so that you have the right mental model early on.


To summarize, if we can identify some work that can be done at the same time as other pieces of work, we can speed up our program's execution. Similarly, if we can finddatawhich can be broken down into independent pieces, then we can perform computations on each piece at the same time as computations on other pieces.


Let's see what this could look like in our MewbileTech application. When we filter through a list of calls, we go through the list and determine which entries match our filter string. What if we could partition the data into N chunks and launch N parallel computing entities (which we callthreads of execution, or simplythreads), each of them responsible for filtering through one of the chunks? The results from each thread could then be "united" at the end and displayed as usual. Given that each thread will process its own separate chunk of data, our filtering should be carried out N times faster, right? Let's try it out!


Task 5.1: Experiment with different amounts of parallelism


We have ignored theVisualizerclass so far, but now it's time to revisit a tiny piece of it, specifically thehandle_window_events()method in theVisualizerclass. We have already implemented the thread creation and distribution of work to each thread for you; have a look over thethreading_wrapper()function, if you are interested. However, your task will be much simpler, and does not require a deep understanding of the threading API.


We want you to vary the number of threads, run the application each time and apply some filters (for example, apply the D filter with a duration under 100 seconds), to get a sense of the benefits of computing filtering in parallel.


To do so, find a variable callednum_threads, currently defined as the value 1. This is the number of chunks that the data is split into, as well as the number of threads launched to filter each of the chunks of data (each thread processes one chunk). Increase this variable's value by doubling it from 1 to 2, 4, then 8, and run the application with each value of the variable. Use the same filter each time, to make sure the measured execution times are comparable. Do you notice any difference in execution time as the number of threads increases?


If you don't notice much difference, don't worry, you're not doing anything wrong! This is solely because, with a tiny dataset, the execution is typically so fast that filtering in parallel won't make much difference.


Task 5.2: Slow things down to magnify the effect


In order to notice faster execution times, either the dataset must be much larger, or the filtering operation has to be much more time consuming than just a basic comparison operation. With a very large dataset, it might take you too long to load the data and experiment with different numbers of threads, so let's try to make the filtering operation more time consuming instead.


Go intofilter.pyand find the code for theDurationFilter. In your implementation of itsapply()method, locate where you check which calls are over or under a given duration. Before making each comparison operation, add a short time delay to make it take longer. To do so, add atime.sleep(T), where T is a time in seconds. We recommend a very short delay, for example, 0.02 seconds; this should be a reasonable number for the size of the dataset indataset.json.


Run your application again, while varying the number of threads to 1, 2, 4, and 8 for each run. Do you notice a difference now? Try with higher values like 16, 32, or 64 threads. Feel free to discuss your findings with your colleagues and try to think about what your results tell you.


Since this task is optional, there are no marks assigned to getting this right, but we want you to reflect on what you notice and get some intuition behind parallel execution. We will discuss this briefly in class once the assignment deadline has passed, and you can learn about this in greater detail in later courses.


Polish!


Take some time to polish up your work. This step will improve your mark, but it also feels so good. Here are some things you can do:


· Pay attention to any violations of the Python style guidelines that PyCharm points out. Fix them!


· In each module, run the providedpython_ta.check_all()code to check for errors. Fix them!


· Check your docstrings to make sure they are precise and complete and that they follow the conventions of the Function Design Recipe and the Class Design Recipe.


· Read through and polish your internal comments.


· Remove any code you added just for debugging, such as print function calls.


· Remove anypassstatement where you have added the necessary code.


· Remove the "TODO" comment wherever you have completed the task.


· Take pride in your gorgeous code!


Submission instructions


1. Login to MarkUs and create a group for the assignment (or specify that you're working alone).


2.
DOES YOUR CODE RUN?! Does it pass your thorough test suite?


3. Submit the filescustomer.py,phoneline.py,contract.py,callhistory.py,filter.py, andapplication.py. Don't submit any other files.


4. On a Lab machine, download all of the files you submitted into a brand-new folder, and test your code once more, thoroughly.Your code will be tested on the Lab machines, so it must run in that environment.


5. Congratulations, you are finished with your firstmajorassignment in CSC148! You are now one step closer to being a wizard/witch who masters parser-tongue. :)

Answered Same DayFeb 20, 2021

Answer To: General guidelines · You may complete this assignment individually or with a partner. · You must not...

Ximi answered on Feb 20 2021
164 Votes
starter_code-2/bill.py
"""
CSC148, Winter 2019
Assignment 1
This code is provided solely for the personal and private use of
students taking the CSC148 course at the University of Toronto.
Copying for purposes other than this use is expressly prohibited.
All forms of distribution of this code, whether as given or with
any changes, are expressly prohibited.
All of the files in this directory and all subdirectories are:
Copyright (c) 2019 Bogdan Simion, Diane Horton, Jacqueline Smith
"""
from typing import Dict, Union
class Bill:
""" A single month's bill for a customer's phone line.
A bill keeps track of the number of calls used each month, along with the
corresponding cost per call.
- The billable minutes and the free minutes are incrementally updated as
calls are loaded from the historic data.
- The billing rate per call and the fixed monthly cost depend on the type
of contract.
The bill does not store the amount due. Instead, the amount due can be
computed on demand by the get_cost() method.
=== Public Attributes ===
billed_min:
number of billable minutes used in the month associated with this bill.
free_min:
number of non-billable minutes used in the month associated with this
bill.
min_rate:
cost for one minute of calling
fixed_cost:
fixed costs for the bill (e.g., fixed monthly cost of the
contract, term deposits, etc.)
type:
type of contract
=== Representation Invariants ===
- billed_min >= 0
- free_min >= 0
- min_rate >= 0
- type: "" | "MTM" | "TERM" | "PREPAID"
"""
billed_min: int
free_min: int
min_rate: float
fixed_cost: float
type: str
def __init__(self) -> None:
""" Create a new Bill.
"""
self.billed_min = 0
self.free_min = 0
self.fixed_cost = 0
self.min_rate = 0
self.type = ""
def set_rates(self, contract_type: str, min_cost: float) \
-> None:
""" Set this Bill's contract type to .
Set this Bill's calling rate to .
"""
self.type = contract_type
self.min_rate = min_cost
def add_fixed_cost(self, cost: float) -> None:
""" Add a fixed one-time cost onto the bill.
"""
self.fixed_cost += cost
def add_billed_minutes(self, minutes: int) -> None:
""" Add minutes as billable minutes
"""
self.billed_min += minutes
def add_free_minutes(self, minutes: int) -> None:
""" Add minutes as free minutes
"""
self.free_min += minutes
def get_cost(self) -> float:
""" Return bill amount, considering the rates for billable calls for
this Bill's contract type.
"""
return self.min_rate * self.billed_min + self.fixed_cost
# ----------------------------------------------------------
# NOTE: You do not need to understand the implementation of
# the following method, to be able to solve this assignment
# but feel free to read it to get a sense of what it does.
# ----------------------------------------------------------
def get_summary(self) -> Dict[str, Union[float, int]]:
""" Return a bill summary as a dictionary containing the bill details.
"""
bill_summary = {'type': self.type,
'fixed': self.fixed_cost,
'free_mins': self.free_min,
'billed_mins': self.billed_min,
'min_rate': self.min_rate,
'total': self.get_cost()
}
return bill_summary
if __name__ == '__main__':
import python_ta
python_ta.check_all(config={
'allowed-import-modules': [
'python_ta', 'typing'
],
'disable': ['R0902'],
'generated-members': 'pygame.*'
})
__MACOSX/starter_code-2/._bill.py
starter_code-2/callhistory.py
"""
CSC148, Winter 2019
Assignment 1
This code is provided solely for the personal and private use of
students taking the CSC148 course at the University of Toronto.
Copying for purposes other than this use is expressly prohibited.
All forms of distribution of this code, whether as given or with
any changes, are expressly prohibited.
All of the files in this directory and all subdirectories are:
Copyright (c) 2019 Bogdan Simion, Diane Horton, Jacqueline Smith
"""
from typing import Dict, List, Tuple
from call import Call
class CallHistory:
"""A class for recording incoming and outgoing calls for a particular number
=== Public Attributes ===
incoming_calls:
Dictionary of incoming calls. Keys are tuples containing a month and a
year, values are a List of Call objects for that month and year.
outgoing_calls:
Dictionary of outgoing calls. Keys are tuples containing a month and a
year, values are a List of Call objects for that month and year.
"""
incoming_calls: Dict[Tuple[int, int], List[Call]]
outgoing_calls: Dict[Tuple[int, int], List[Call]]
def __init__(self) -> None:
""" Create an empty CallHistory.
"""
self.outgoing_calls = {}
self.incoming_calls = {}
def register_outgoing_call(self, call: Call) -> None:
""" Register a Call into this outgoing call history
"""
# TODO: Implement this method. # i have already done it not sure if its right
if self.outgoing_calls == {}:
self.outgoing_calls[call.get_bill_date()[0]] = [call]
elif call.get_bill_date()[0] in self.outgoing_calls.keys():
self.outgoing_calls[call.get_bill_date()[0]].append(call)
def register_incoming_call(self, call: Call) -> None:
""" Register a Call into this incoming call history
"""
# TODO: Implement this method. # i have already done it not sure if its right
if self.incoming_calls == {}:
self.incoming_calls[call.get_bill_date()[0]] = [call]
elif call.get_bill_date()[0] in self.incoming_calls.keys():
self.incoming_calls[call.get_bill_date()[0]].append(call)
# ----------------------------------------------------------
# NOTE: You do not need to understand the implementation of
# the following methods, to be able to solve this assignment
# but feel free to read them to get a sense of what these do.
# ----------------------------------------------------------
def get_monthly_history(self, month: int = None, year: int = None) -> \
Tuple[List[Call], List[Call]]:
""" Return all outgoing and incoming calls for and ,
as a Tuple containing two lists in the following order:
(outgoing calls, incoming calls)
If and are both None, then return all calls from this
call history.
Precondition:
- and are either both specified, or are both missing/None
- if and are specified (non-None), they are both valid
monthly cycles according to the input dataset
"""
monthly_history = ([], [])
if month is not None and year is not None:
if (month, year) in self.outgoing_calls:
for call in self.outgoing_calls[(month, year)]:
monthly_history[0].append(call)
if (month, year) in self.incoming_calls:
for call in self.incoming_calls[(month, year)]:
monthly_history[1].append(call)
else:
for entry in self.outgoing_calls:
for call in self.outgoing_calls[entry]:
monthly_history[0].append(call)
for entry in self.incoming_calls:
for call in self.incoming_calls[entry]:
monthly_history[1].append(call)
return monthly_history
if __name__ == '__main__':
import python_ta
python_ta.check_all(config={
'allowed-import-modules': [
'python_ta', 'typing', 'datetime', 'call'
''
],
'disable': ['R0902', 'R0913'],
'generated-members': 'pygame.*'
})
__MACOSX/starter_code-2/._callhistory.py
starter_code-2/StarterCodeArchitecture.pdf
__MACOSX/starter_code-2/._StarterCodeArchitecture.pdf
starter_code-2/__pycache__/customer.cpython-37.pyc
starter_code-2/__pycache__/callhistory.cpython-37.pyc
starter_code-2/__pycache__/call.cpython-37.pyc
__MACOSX/starter_code-2/__pycache__/._call.cpython-37.pyc
starter_code-2/__pycache__/application.cpython-37.pyc
__MACOSX/starter_code-2/__pycache__/._application.cpython-37.pyc
starter_code-2/__pycache__/bill.cpython-37.pyc
__MACOSX/starter_code-2/__pycache__/._bill.cpython-37.pyc
starter_code-2/__pycache__/filter.cpython-37.pyc
starter_code-2/__pycache__/phoneline.cpython-37.pyc
starter_code-2/__pycache__/visualizer.cpython-37.pyc
__MACOSX/starter_code-2/__pycache__/._visualizer.cpython-37.pyc
starter_code-2/__pycache__/contract.cpython-37.pyc
__MACOSX/starter_code-2/.___pycache__
starter_code-2/application.py
"""
CSC148, Winter 2019
Assignment 1
This code is provided solely for the personal and private use of
students taking the CSC148 course at the University of Toronto.
Copying for purposes other than this use is expressly prohibited.
All forms of distribution of this code, whether as given or with
any changes, are expressly prohibited.
All of the files in this directory and all subdirectories are:
Copyright (c) 2019 Bogdan Simion, Diane Horton, Jacqueline Smith
"""
import datetime
import json
from typing import List, Dict
from visualizer import Visualizer
from customer import Customer
from phoneline import PhoneLine
from call import Call
from contract import PrepaidContract, MTMContract, TermContract
def import_data() -> Dict[str, List[Dict]]:
""" Open the file which stores the json data, and return
a dictionary that stores this data in a format as described in the A1
handout.
Precondition: the dataset file must be in the json format.
"""
log = {}
with open("dataset.json") as o:
log = json.load(o)
return log
def create_customers(log: Dict[str, List[Dict]]) -> List[Customer]:
""" Returns a list of Customer instances for each customer from the input
dataset from the dictionary .
Precondition:
- The dictionary contains the input data in the correct format,
matching the expected input format described in the handout.
"""
customer_list = []
for cust in log['customers']:
customer = Customer(cust['id'])
for line in cust['lines']:
contract = None
# TODO:
# 1) Uncomment the piece of code below once you've implemented
# all types of contracts.
# 2) Make sure to import the necessary contract classes in this file
# 3) Remove this TODO list when you're done.

if line['contract'] == 'prepaid':
# start with $100 credit on the account
contract = PrepaidContract(datetime.date(2017, 12, 25), 100)
elif line['contract'] == 'mtm':
contract = MTMContract(datetime.date(2017, 12, 25))
elif line['contract'] == 'term':
contract = TermContract(datetime.date(2017, 12, 25),
datetime.date(2019, 6, 25))
else:
print("ERROR: unknown contract type")

line = PhoneLine(line['number'], contract)
customer.add_phone_line(line)
customer_list.append(customer)
return customer_list
def find_customer_by_number(number: str, customer_list: List[Customer]) \
-> Customer:
""" Return the Customer with the phone number in the list of
customers .
If the number does not belong to any customer, return None.
"""
cust = None
for customer in customer_list:
if number in customer:
cust = customer
return cust
def new_month(customer_list: List[Customer], month: int, year: int) -> None:
""" Advance all customers in to a new month of their
contract, as specified by the and arguments.
"""
for cust in customer_list:
cust.new_month(month, year)
def process_event_history(log: Dict[str, List[Dict]],
customer_list: List[Customer]) -> None:
""" Process the calls from the dictionary. The
list contains all the customers that exist in the dictionary.
Construct Call objects from and register the Call into the
corresponding customer's call history.
Hint: You must advance all customers to a new month using the new_month()
function, everytime a new month is detected for the current event you are
extracting.
Preconditions:
- All calls are ordered chronologically (based on the call's date and time),
when retrieved from the dictionary , as specified in the handout.
- The argument guarantees that there is no "gap" month with zero
activity for ALL customers, as specified in the handout.
- The dictionary is in the correct format, as defined in the
handout.
- The already contains all the customers from the .
"""
# TODO: Implement this method. We are giving you the first few lines of code
billing_date = datetime.datetime.strptime(log['events'][0]['time'],
"%Y-%m-%d %H:%M:%S")
billing_month = billing_date.month
# start recording the bills from this date
# Note: uncomment the following lines when you're ready to implement this
new_month(customer_list, billing_date.month, billing_date.year)
for event_data in log['events']:
if event_data['type'] == "call":
time = datetime.datetime.strptime(event_data['time'],
"%Y-%m-%d %H:%M:%S")
calls = Call(event_data['src_number'],event_data['dst_number'],time,
event_data['duration'],tuple(event_data['src_loc']),
tuple(event_data['dst_loc']))
caller = find_customer_by_number(event_data['src_number'], customer_list)
receiver = find_customer_by_number(event_data['dst_number'], customer_list)
caller.make_call(calls)
receiver.receive_call(calls)
if __name__ == '__main__':
v = Visualizer()
print("Toronto map coordinates:")
print(" Lower-left corner: -79.697878, 43.576959")
print(" Upper-right corner: -79.196382, 43.799568")
input_dictionary = import_data()
customers = create_customers(input_dictionary)
process_event_history(input_dictionary, customers)
# ----------------------------------------------------------------------
# NOTE: You do not need to understand any of the implementation below,
# to be able to solve this assignment. However, feel free to
# read it an
yway, just to get a sense of how the application runs.
# ----------------------------------------------------------------------
# Gather all calls to be drawn on screen for filtering, but we only want
# to plot each call only once, so only plot the outgoing calls to screen.
# (Each call is registered as both an incoming and outgoing)
all_calls = []
for c in customers:
hist = c.get_history()
all_calls.extend(hist[0])
print("\n-----------------------------------------")
print("Total Calls in the dataset:", len(all_calls))
# Main loop for the application.
# 1) Wait for user interaction with the system and processes everything
# appropriately
# 2) Take the calls from the results of the filtering and create the
# drawables and connection lines for those calls
# 3) Display the calls in the visualization window
events = all_calls
while not v.has_quit():
events = v.handle_window_events(customers, events)
connections = []
drawables = []
for event in events:
connections.append(event.get_connection())
drawables.extend(event.get_drawables())
# Put the connections on top of the other sprites
drawables.extend(connections)
v.render_drawables(drawables)
import python_ta
python_ta.check_all(config={
'allowed-import-modules': [
'python_ta', 'typing', 'json', 'datetime',
'visualizer', 'customer', 'call', 'contract', 'phoneline'
],
'allowed-io': [
'create_customers', 'import_data'
],
'generated-members': 'pygame.*'
})
__MACOSX/starter_code-2/._application.py
starter_code-2/phoneline.py
"""
CSC148, Winter 2019
Assignment 1
This code is provided solely for the personal and private use of
students taking the CSC148 course at the University of Toronto.
Copying for purposes other than this use is expressly prohibited.
All forms of distribution of this code, whether as given or with
any changes, are expressly prohibited.
All of the files in this directory and all subdirectories are:
Copyright (c) 2019 Bogdan Simion, Diane Horton, Jacqueline Smith
"""
from typing import List, Dict, Tuple, Optional, Union
from call import Call
from callhistory import CallHistory
from bill import Bill
from contract import Contract
class PhoneLine:
""" MewbileTech customer's phone line.
=== Public Attributes ===
number:
phone number
contract:
current contract for this phone, represented by a Contract instance
bills:
dictionary containing all the bills for this phoneline
each key is a (month, year) tuple and the corresponding value is
the Bill object for that month+year date.
callhistory:
call history for this phone line, represented as a CallHistory object
=== Representation Invariants ===
- the dictionary contains as keys only those month+year combinations
for dates that are encountered at least in one call from the input dataset.
"""
number: str
contract: Contract
bills: Dict[Tuple[int, int], Bill]
callhistory: CallHistory
def __init__(self, number: str, contract: Contract) -> None:
""" Create a new PhoneLine with and .
"""
self.number = number
self.contract = contract
self.callhistory = CallHistory()
self.bills = {}
def new_month(self, month: int, year: int) -> None:
""" Advance to a new month (specified by and ) in the
contract corresponding to this phone line.
If the new month+year does not already exist in the attribute,
create a new bill.
"""
if (month, year) not in self.bills:
self.bills[(month, year)] = Bill()
self.contract.new_month(month, year, self.bills[(month, year)])
def make_call(self, call: Call) -> None:
""" Add the to this phone line's callhistory, and bill it
according to the contract for this phone line.
If there is no bill for the current monthly billing cycle, then a new
month must be by advancing to the right month from .
"""
# TODO: Implement this method
self.callhistory.register_outgoing_call(call)
dates = call.get_bill_date()
if self.get_bill(dates[0],dates[1]) == None:
self.new_month(dates[0],dates[1])

def receive_call(self, call: Call) -> None:
""" Add the to this phone line's callhistory.
Incoming calls are not billed under any contract.
However, if there is no bill for the current monthly billing cycle,
then a new month must be by advancing to the right month from
.
"""
# TODO: Implement this method
self.callhistory.register_incoming_call(call)
dates = call.get_bill_date()
if self.get_bill(dates[0],dates[1]) == None:
self.new_month(dates[0],dates[1])
def cancel_line(self) -> float:
""" Cancel this line's contract and return the outstanding bill amount
"""
return self.contract.cancel_contract()
# ----------------------------------------------------------
# NOTE: You do not need to understand the implementation of
# the following methods, to be able to solve this assignment
# but feel free to read them to get a sense of what these do.
# ----------------------------------------------------------
def get_number(self) -> str:
""" Return the phone number for this line
"""
return self.number
def get_call_history(self) -> CallHistory:
""" Return the CallHistory for this line
"""
return self.callhistory
def get_monthly_history(self, month: int = None, year: int = None) -> \
Tuple[List[Call], List[Call]]:
""" Return all calls this line has made during the month of the
year, formatted as a Tuple containing two lists, in this order:
outgoing calls, incoming calls
If month and year are both None, then return all calls from the
callhistory of this phone line.
Precondition:
- and are either both specified, or are both missing/None
- if and are specified (non-None), they are both valid
monthly cycles according to the input dataset
"""
return self.callhistory.get_monthly_history(month, year)
def get_bill(self, month: int, year: int) \
-> Optional[Dict[str, Union[float, int]]]:
""" Return a bill summary for the + billing cycle, as a
dictionary.
This dictionary will include the following string keys:
"number" - indicates the phone number
"type" - indicates the contract type
"fixed" - fixed cost for that month
"free_mins" - number of free minutes used in this monthly cycle
"billed_mins" - number of billed minutes used in this monthly cycle
"min_rate" - billing rate per minute
"total" - total cost for this monthly bill
The values corresponding to each key represent the respective amounts.
If no bill exists for this month+year, return None.
"""
if (month, year) not in self.bills:
return None
bill_summary = self.bills[(month, year)].get_summary()
bill_summary['number'] = self.number
return bill_summary
if __name__ == '__main__':
import python_ta
python_ta.check_all(config={
'allowed-import-modules': [
'python_ta', 'typing',
'call', 'callhistory', 'bill', 'contract'
],
'generated-members': 'pygame.*'
})
__MACOSX/starter_code-2/._phoneline.py
starter_code-2/filter.py
"""
CSC148, Winter 2019
Assignment 1
This code is provided solely for the personal and private use of
students taking the CSC148 course at the University of Toronto.
Copying for purposes other than this use is expressly prohibited.
All forms of distribution of this code, whether as given or with
any changes, are expressly prohibited.
All of the files in this directory and all subdirectories are:
Copyright (c) 2019 Bogdan Simion, Diane Horton, Jacqueline Smith
"""
import time
import datetime
from typing import List, Tuple
from call import Call
from customer import Customer
class Filter:
""" A class for filtering customer data on some criterion. A filter is
applied to a set of calls.
This is an abstract class. Only subclasses should be instantiated.
"""
def __init__(self) -> None:
pass
def apply(self, customers: List[Customer],
data: List[Call],
filter_string: str) \
-> List[Call]:
""" Return a list of all calls from , which match the filter
specified in .
The is provided by the user through the visual prompt,
after selecting this filter.
The is a list of all customers from the input dataset.
If the filter has
no effect or the is invalid then return the same calls
from the input.
Precondition:
- contains the list of all customers from the input dataset
- all calls included in are valid calls from the input dataset
"""
raise NotImplementedError
def __str__(self) -> str:
""" Return a description of this filter to be displayed in the UI menu
"""
raise NotImplementedError
class ResetFilter(Filter):
"""
A class for resetting all previously applied filters, if any.
"""
def apply(self, customers: List[Customer],
data: List[Call],
filter_string: str) \
-> List[Call]:
""" Reset all of the applied filters. Return a List containing all the
calls corresponding to .
The and arguments for this type of filter are
ignored.
Precondition:
- contains the list of all customers from the input dataset
"""
filtered_calls = []
for c in customers:
customer_history = c.get_history()
# only take outgoing calls, we don't want to include calls twice
filtered_calls.extend(customer_history[0])
return filtered_calls
def __str__(self) -> str:
""" Return a description of this filter to be displayed in the UI menu
"""
return "Reset all of the filters applied so far, if any"
class CustomerFilter(Filter):
"""
A class for selecting only the calls from a given customer.
"""
def apply(self, customers: List[Customer],
data: List[Call],
filter_string: str) \
-> List[Call]:
""" Return a list of all calls from made or received by the
customer with the id specified in .
The list contains all customers from the input dataset.
The filter string is valid if and only if it contains a valid
customer ID.
- If the filter string is invalid, return the original list
- If the filter string is invalid, your code must not crash, as
specified in the handout.
"""

filtered_calls = []
c_ids = [c._id for c in customers]
if filter_string not in c_ids:
return data
else:
for c in customers:
if filter_string == c._id:
customer_history = c.get_history()
# only take outgoing calls, we don't want to include calls twice
if customer_history[0] in data:
filtered_calls.extend(customer_history[0])
return filtered_calls

def __str__(self) -> str:
""" Return a description of this filter to be displayed in the UI menu
"""
return "Filter events based on customer ID"
class DurationFilter(Filter):
"""
A class for selecting only the calls lasting either over or under a
specified duration.
"""
def apply(self, customers: List[Customer],
data: List[Call],
filter_string: str) \
-> List[Call]:
""" Return a list of all calls from with a duration of under or
over the time indicated in the .
The list contains all customers from the input dataset.
The filter string is valid if and only if it contains the following
input format: either "Lxxx" or "Gxxx", indicating to filter calls less
than xxx or greater than xxx seconds, respectively.
- If the filter string is invalid, return the original list
- If the filter string is invalid, your code must not crash, as
specified in the handout.
"""

filtered_calls = []

if not filter_string.startswith('L') or not filter_string.startswith('G'):
return data
elif filter_string.startswith('L'):
for call in data:
if call.duration < int(filter_string.split('L')[1]):

filtered_calls.extend(call)
elif filter_string.startswith('G'):
if call.duration > int(filter_string.split('G')[1]):
filtered_calls.extend(call)
return filtered_calls

def __str__(self) -> str:
""" Return a description of this filter to be displayed in the UI menu
"""
return "Filter calls based on duration; " \
"L### returns calls less than specified length, G### for greater"
class LocationFilter(Filter):
"""
A class for selecting only the calls that took place within a specific area
"""
def apply(self, customers: List[Customer],
data: List[Call],
filter_string: str) \
-> List[Call]:
""" Return a list of all calls from , which took place within
a location specified by the (at least the source or the
destination of the event was in the range of coordinates from the
).
The list contains all customers from the input dataset.
The filter string is valid if and only if it contains four valid
coordinates within the map boundaries.
These coordinates represent the location of the lower left corner
and the upper right corner of the search location rectangle,
as 2 pairs of longitude/latitude coordinates, each separated by
a comma and a space:
lowerLong, lowerLat, upperLong, upperLat
Calls that fall exactly on the boundary of this rectangle are
considered a match as well.
- If the filter string is invalid, return the original list
- If the filter string is invalid, your code must not crash, as
specified in the handout.
"""

filtered_calls = []
if len(filter_string.split(',')) < 4:
return data
else:
coords = map(int, filter_string.split(','))
for call in data:
lat, lng = call.src_loc
if lng > coords[0] and lat > coords[1] and lng < coords[2] \
and lat < coords[3]:
filtered_calls.extend(call)
return data
def __str__(self) -> str:
""" Return a description of this filter to be displayed in the UI menu
"""
return "Filter calls made or received in a given rectangular area. " \
"Format: \"lowerLong, lowerLat, " \
"upperLong, upperLat\" (e.g., -79.6, 43.6, -79.3, 43.7)"
if __name__ == '__main__':
import python_ta
python_ta.check_all(config={
'allowed-import-modules': [
'python_ta', 'typing', 'time', 'datetime', 'call', 'customer'
],
'max-nested-blocks': 4,
'allowed-io': ['apply', '__str__'],
'disable': ['W0611', 'W0703'],
'generated-members': 'pygame.*'
})
__MACOSX/starter_code-2/._filter.py
starter_code-2/customer.py
"""
CSC148, Winter 2019
Assignment 1
This code is provided solely for the personal and private use of
students taking the CSC148 course at the University of Toronto.
Copying for purposes other than this use is expressly prohibited.
All forms of distribution of this code, whether as given or with
any changes, are expressly prohibited.
All of the files in this directory and all subdirectories are:
Copyright (c) 2019 Bogdan Simion, Diane Horton, Jacqueline Smith
"""
from typing import List, Union, Tuple, Dict
from phoneline import PhoneLine
from call import Call
from callhistory import CallHistory
class Customer:
""" A MewbileTech customer.
"""
# === Private Attributes ===
# _id:
# this customer's 4 digit Customer id
# _phone_lines:
# this customer's phone lines
_id: int
_phone_lines: List[PhoneLine]
def __init__(self, cid: int) -> None:
""" Create a new Customer with the id
"""
self._id = cid
self._phone_lines = []
def new_month(self, month: int, year: int) -> None:
""" Advance to a new month (specified by and ) in the
contracts for each phone line that this customer owns.
Note: we don't care about payments; we assume that this customer pays
the bill amount in full for the previous month.
"""
for line in self._phone_lines:
line.new_month(month, year)
def make_call(self, call: Call) -> None:
""" Record that a call was made from the source phone number of .
Precondition: The phone line associated with the source phone number of
, is owned by this customer
"""
# TODO: Implement this method
for phone_line in self._phone_lines:
if phone_line.number == call.src_number:
phone_line.make_call(call)
def receive_call(self, call: Call) -> None:
""" Record that a call was made to the destination phone number of
.
Precondition: The phone line associated with the destination phone
number of , is owned by this customer
"""
# TODO: Implement this method
for phone_line in self._phone_lines:
if phone_line.number == call.dst_number:
phone_line.receive_call(call)
def cancel_phone_line(self, number: str) -> Union[float, None]:
""" Remove PhoneLine with number from this customer and return
the amount still owed by this customer.
Return None if is not owned by this customer.
"""
fee = None
for pl in self._phone_lines:
if pl.get_number() == number:
self._phone_lines.remove(pl)
fee = pl.cancel_line()
return fee
# ----------------------------------------------------------
# NOTE: You do not need to understand the implementation of
# the following methods, to be able to solve this assignment
# but feel free to read them to get a sense of what these do.
# ----------------------------------------------------------
def add_phone_line(self, pline: PhoneLine) -> None:
""" Add a new PhoneLine to this customer.
"""
self._phone_lines.append(pline)
def get_phone_numbers(self) -> List[str]:
""" Return a list of all of the numbers this customer owns
"""
numbers = []
for line in self._phone_lines:
numbers.append(line.get_number())
return numbers
def get_id(self) -> int:
""" Return the id for this customer
"""
return self._id
def __contains__(self, item: str) -> bool:
""" Check if this customer owns the phone number
"""
contains = False
for line in self._phone_lines:
if line.get_number() == item:
contains = True
return contains
def generate_bill(self, month: int, year: int) \
-> Tuple[int, float, List[Dict]]:
""" Return a bill summary for the and billing cycle,
as a Tuple containing the customer id, total cost for all phone lines,
and a List of bill summaries generated for each phone line.
"""
bills = []
total = 0
for l in self._phone_lines:
line_bill = l.get_bill(month, year)
if line_bill is not None:
bills.append(line_bill)
total += line_bill['total']
return self._id, total, bills
def print_bill(self, month: int, year: int) -> None:
""" Print the bill for the and billing cycle, to the
console.
Precondition:
- and correspond to a valid bill for this customer.
That is, the month and year cannot be outside the range of the historic
records from the input dataset.
"""
bill_data = self.generate_bill(month, year)
print("========= BILL ===========")
print("Customer id: " + str(self._id) + " month: " +
str(month) + "/" + str(year))
print("Total due this month: {0:.2f}".format(bill_data[1]))
for line in bill_data[2]:
print("\tnumber: " + line['number'] + " type: " + line['type'])
print("==========================")
def get_history(self) \
-> Tuple[List[Call], List[Call]]:
""" Return all the calls from the call history of this
customer, as a tuple in the following format:
(outgoing calls, incoming calls)
"""
history = ([], [])
for line in self._phone_lines:
line_history = line.get_monthly_history()
history[0].extend(line_history[0])
history[1].extend(line_history[1])
return history
def get_call_history(self, number: str = None) -> List[CallHistory]:
""" Return the call history for , stored into a list.
If is not provided, return a list of all call histories for all
phone lines owned by this customer.
"""
history = []
for line in self._phone_lines:
if number is not None:
if line.get_number() == number:
history.append(line.get_call_history())
else:
history.append(line.get_call_history())
return history
if __name__ == '__main__':
import python_ta
python_ta.check_all(config={
'allowed-import-modules': [
'python_ta', 'typing', 'phoneline', 'call', 'callhistory'
],
'allowed-io': ['print_bill'],
'disable': ['R0902', 'R0913'],
'generated-members': 'pygame.*'
})
__MACOSX/starter_code-2/._customer.py
starter_code-2/call.py
"""
CSC148, Winter 2019
Assignment 1
This code is provided solely for the personal and private use of
students taking the CSC148 course at the University of Toronto.
Copying for purposes other than this use is expressly prohibited.
All forms of distribution of this code, whether as given or with
any changes, are expressly prohibited.
All of the files in this directory and all subdirectories are:
Copyright (c) 2019 Bogdan Simion, Diane Horton, Jacqueline Smith
"""
import datetime
import os
from typing import Tuple, List, Optional
import pygame
# Sprite files to display the start and end of a call
START_CALL_SPRITE = 'data/call-start-2.png'
END_CALL_SPRITE = 'data/call-end-2.png'
# ----------------------------------------------------------------------------
# NOTE: You do not need to understand the implementation of the Drawable class
# to be able to solve this assignment. However, feel feel free to read it for
# the fun of understanding the visualization system.
# ----------------------------------------------------------------------------
class Drawable:
"""A class for objects that the graphical renderer can draw.
=== Public Attributes ===
sprite:
image object for this drawable or None.
If none, then must have linelimits
linelimits:
limits for the line of the connection or None.
If none, then must have sprite
loc: location (longitude/latitude pair)
"""
sprite: Optional[pygame.Surface]
linelimits: Optional[Tuple[float, float]]
loc: Optional[Tuple[float, float]]
def __init__(self, sprite_file: Optional[str] = None,
location: Optional[Tuple[float, float]] = None,
linelimits: Optional[Tuple[Tuple[float, float],
Tuple[float, float]]] = None) \
-> None:
"""Initialize this drawable object with the ,
and .
"""
self.linelimits = None
self.sprite = None
self.loc = None
if sprite_file is not None and location is not None:
self.sprite = pygame.transform.smoothscale(
pygame.image.load(os.path.join(os.path.dirname(__file__),
sprite_file)), (13, 13))
self.loc = location
else:
self.linelimits = linelimits
def get_position(self) -> Tuple[float, float]:
"""Return the (long, lat) position of this object at the given time.
"""
return self.loc
def get_linelimits(self) -> Optional[Tuple[float, float]]:
"""Return the limits for the line if the drawable is a line type
(otherwise None)
"""
return self.linelimits
class Call:
""" A call made by a customer to another customer.
=== Public Attributes ===
src_number:
source number for this Call
dst_number:
destination number for this Call
time:
date and time of this Call
duration:
duration in seconds for this Call
src_loc:
location of the source of this Call; a Tuple containing the longitude
and latitude coordinates
dst_loc:
location of the destination of this Call; a Tuple containing the
longitude and latitude coordinates
drawables:
sprites for drawing the source and destination of this Call
connection:
connecting line between the two sprites representing the source and
destination of this Call
=== Representation Invariants ===
- duration >= 0
"""
src_number: str
dst_number: str
time: datetime.datetime
duration: int
src_loc: Tuple[float, float]
dst_loc: Tuple[float, float]
drawables: List[Drawable]
connection: Drawable
def __init__(self, src_nr: str, dst_nr: str,
calltime: datetime.datetime, duration: int,
src_loc: Tuple[float, float], dst_loc: Tuple[float, float]) \
-> None:
""" Create a new Call object with the given parameters.
"""
self.src_number = src_nr
self.dst_number = dst_nr
self.time = calltime
self.duration = duration
self.src_loc = src_loc
self.dst_loc = dst_loc
self.drawables = [Drawable(sprite_file=START_CALL_SPRITE,
location=src_loc),
Drawable(sprite_file=END_CALL_SPRITE,
location=dst_loc)]
self.connection = Drawable(linelimits=(src_loc, dst_loc))
def get_bill_date(self) -> Tuple[int, int]:
""" Return the billing date for this Call, as a tuple containing the
month and the year
"""
return self.time.month, self.time.year
# ----------------------------------------------------------
# NOTE: You do not need to understand the implementation of
# the following methods, to be able to solve this assignment
# but feel free to read them to get a sense of what these do.
# ----------------------------------------------------------
def get_drawables(self) -> List[Drawable]:
""" Return the list of drawable sprites for this Call
"""
return self.drawables
def get_connection(self) -> Drawable:
""" Return the connecting line for this Call start and end locations
"""
return self.connection
if __name__ == '__main__':
import python_ta
python_ta.check_all(config={
'allowed-import-modules': [
'python_ta', 'typing', 'datetime', 'os', 'pygame'
],
'disable': ['R0902', 'R0913'],
'generated-members': 'pygame.*'
})
__MACOSX/starter_code-2/._call.py
starter_code-2/contract.py
"""
CSC148, Winter 2019
Assignment 1
This code is provided solely for the personal and private use of
students taking the CSC148 course at the University of Toronto.
Copying for purposes other than this use is expressly prohibited.
All forms of distribution of this code, whether as given or with
any changes, are expressly prohibited.
All of the files in this directory and all subdirectories are:
Copyright (c) 2019 Bogdan Simion, Diane Horton, Jacqueline Smith
"""
import datetime
from math import ceil
from typing import Optional
from bill import Bill
from call import Call
# Constants for the month-to-month contract monthly fee and term deposit
MTM_MONTHLY_FEE = 50.00
TERM_MONTHLY_FEE = 20.00
TERM_DEPOSIT = 300.00
# Constants for the included minutes and SMSs in the term contracts (per month)
TERM_MINS = 100
# Cost per minute and per SMS in the month-to-month contract
MTM_MINS_COST = 0.05
# Cost per minute and per SMS in the term contract
TERM_MINS_COST = 0.1
# Cost per minute and per SMS in the prepaid contract
PREPAID_MINS_COST = 0.025
class Contract:
""" A contract for a phone line
This is an abstract class. Only subclasses should be instantiated.
=== Public Attributes ===
start:
starting date for the contract
bill:
bill for this contract for the last month of call records loaded from
the input dataset
"""
start: datetime.datetime
bill: Optional[Bill]
def __init__(self, start: datetime.date) -> None:
""" Create a new Contract with the date, starts as inactive
"""
self.start = start
self.bill = None
def new_month(self, month: int, year: int, bill: Bill) -> None:
""" Advance to a new month in the contract, corresponding to and
. This may be the first month of the contract.
Store the argument in this contract and set the appropriate rate
per minute and fixed cost.
"""
raise NotImplementedError
def bill_call(self, call: Call) -> None:
""" Add the to the bill.
Precondition:
- a bill has already been created for the month+year when the
was made. In other words, you can safely assume that self.bill has been
already advanced to the right month+year.
"""
self.bill.add_billed_minutes(ceil(call.duration / 60.0))
def cancel_contract(self) -> float:
""" Return the amount owed in order to close the phone line associated
with this contract.
Precondition:
- a bill has already been created for the month+year when this contract
is being cancelled. In other words, you can safely assume that self.bill
exists for the right month+year when the cancelation is requested.
"""
self.start = None
return self.bill.get_cost()
# TODO: Implement the MTMContract, TermContract, and PrepaidContract
class TermContract(Contract):
def __init__(self, start: datetime.date, end: datetime.date) -> None:
self.start = end
self.end = end
Contract.__init__(self, start)
def new_month(self, month: int, year: int, bill: Bill) -> None:
""" Advance to a new month in the contract, corresponding to and
. This may be the first month of the contract.
Store the argument in this contract and set the appropriate rate
per minute and fixed cost.
"""
self.bill = bill
self.bill.set_rates('TERM', TERM_MINS_COST)
self.bill.add_fixed_cost(TERM_DEPOSIT)
self.bill.add_free_minutes(TERM_MINS)
class MTMContract(Contract):
def __init__(self, start: datetime.date) -> None:
self.start = start
Contract.__init__(self, start)
def new_month(self, month: int, year: int, bill: Bill) -> None:
""" Advance to a new month in the contract, corresponding to and
. This may be the first month of the contract.
Store the argument in this contract and set the appropriate rate
per minute and fixed cost.
"""
self.bill = bill
self.bill.set_rates('MTM', MTM_MINS_COST)
self.bill.add_fixed_cost(MTM_MONTHLY_FEE)
class PrepaidContract(Contract):
def __init__(self, start: datetime.date, credit) -> None:
self.start = start
self.credit = credit
Contract.__init__(self, start)
def new_month(self, month: int, year: int, bill: Bill) -> None:
""" Advance to a new month in the contract, corresponding to and
. This may be the first month of the contract.
Store the argument in this contract and set the appropriate rate
per minute and fixed cost.
"""
self.bill = bill
self.bill.set_rates('PREPAID', PREPAID_MINS_COST)
self.bill.add_fixed_cost(self.credit)
if __name__ == '__main__':
import python_ta
python_ta.check_all(config={
'allowed-import-modules': [
'python_ta', 'typing', 'datetime', 'bill', 'call', 'math'
],
'disable': ['R0902', 'R0913'],
'generated-members': 'pygame.*'
})
__MACOSX/starter_code-2/._contract.py
starter_code-2/data/call-end-2.png
__MACOSX/starter_code-2/data/._call-end-2.png
starter_code-2/data/call-start-2.png
__MACOSX/starter_code-2/data/._call-start-2.png
starter_code-2/data/toronto_map.png
__MACOSX/starter_code-2/data/._toronto_map.png
starter_code-2/data/call-end.png
__MACOSX/starter_code-2/data/._call-end.png
starter_code-2/data/call-start.png
__MACOSX/starter_code-2/data/._call-start.png
__MACOSX/starter_code-2/._data
starter_code-2/sample_tests.py
"""
CSC148, Winter 2019
Assignment 1
This code is provided solely for the personal and private use of
students taking the CSC148 course at the University of Toronto.
Copying for purposes other than this use is expressly prohibited.
All forms of distribution of this code, whether as given or with
any changes, are expressly prohibited.
All of the files in this directory and all subdirectories are:
Copyright (c) 2019 Bogdan Simion, Diane Horton, Jacqueline Smith
"""
import datetime
import pytest
from application import create_customers, process_event_history
from customer import Customer
from contract import TermContract, MTMContract, PrepaidContract
from phoneline import PhoneLine
from filter import DurationFilter, CustomerFilter, ResetFilter
"""
This is a sample test file with a limited set of cases, which are similar in
nature to the full autotesting suite
Use this framework to check some of your work and as a starting point for
creating your own tests
*** Passing these tests does not mean that it will necessarily pass the
autotests ***
"""
def create_single_customer_with_all_lines() -> Customer:
""" Create a customer with one of each type of PhoneLine
"""
contracts = [
TermContract(start=datetime.date(year=2017, month=12, day=25),
end=datetime.date(year=2019, month=6, day=25)),
MTMContract(start=datetime.date(year=2017, month=12, day=25)),
PrepaidContract(start=datetime.date(year=2017, month=12, day=25),
balance=100)
]
numbers = ['867-5309', '273-8255', '649-2568']
customer = Customer(cid=5555)
for i in range(len(contracts)):
customer.add_phone_line(PhoneLine(numbers[i], contracts[i]))
customer.new_month(12, 2017)
return customer
test_dict = {'events': [
{"type": "sms",
"src_number": "867-5309",
"dst_number": "273-8255",
"time": "2018-01-01 01:01:01",
"src_loc": [-79.42848154284123, 43.641401675960374],
"dst_loc": [-79.52745693913239, 43.750338501653374]},
{"type": "sms",
"src_number": "273-8255",
"dst_number": "649-2568",
"time": "2018-01-01 01:01:02",
"src_loc": [-79.42848154284123, 43.641401675960374],
"dst_loc": [-79.52745693913239, 43.750338501653374]},
{"type": "sms",
"src_number": "649-2568",
"dst_number": "867-5309",
"time": "2018-01-01 01:01:03",
"src_loc": [-79.42848154284123, 43.641401675960374],
"dst_loc": [-79.52745693913239, 43.750338501653374]},
{"type": "call",
"src_number": "273-8255",
"dst_number": "867-5309",
"time": "2018-01-01 01:01:04",
"duration": 10,
"src_loc": [-79.42848154284123, 43.641401675960374],
"dst_loc": [-79.52745693913239, 43.750338501653374]},
{"type": "call",
"src_number": "867-5309",
"dst_number": "649-2568",
"time": "2018-01-01 01:01:05",
"duration": 50,
"src_loc": [-79.42848154284123, 43.641401675960374],
"dst_loc": [-79.52745693913239, 43.750338501653374]},
{"type": "call",
"src_number": "649-2568",
"dst_number": "273-8255",
"time": "2018-01-01 01:01:06",
"duration": 50,
"src_loc": [-79.42848154284123, 43.641401675960374],
"dst_loc": [-79.52745693913239, 43.750338501653374]}
],
'customers': [
{'lines': [
{'number': '867-5309',
'contract': 'term'},
{'number': '273-8255',
'contract': 'mtm'},
{'number': '649-2568',
'contract': 'prepaid'}
],
'id': 5555}
]
}
def test_customer_creation() -> None:
""" Test for the correct creation of Customer, PhoneLine, and Contract
classes
"""
customer = create_single_customer_with_all_lines()
bill = customer.generate_bill(12, 2017)
assert len(customer.get_phone_numbers()) == 3
assert len(bill) == 3
assert bill[0] == 5555
assert bill[1] == 270.0
assert len(bill[2]) == 3
assert bill[2][0]['total'] == 320
assert bill[2][1]['total'] == 50
assert bill[2][2]['total'] == -100
# Check for the customer creation in application.py
customer = create_customers(test_dict)[0]
customer.new_month(12, 2017)
bill = customer.generate_bill(12, 2017)
assert len(customer.get_phone_numbers()) == 3
assert len(bill) == 3
assert bill[0] == 5555
assert bill[1] == 270.0
assert len(bill[2]) == 3
assert bill[2][0]['total'] == 320
assert bill[2][1]['total'] == 50
assert bill[2][2]['total'] == -100
def test_events() -> None:
""" Test the ability to make calls, and ensure that the CallHistory objects
are populated
"""
customers = create_customers(test_dict)
customers[0].new_month(1, 2018)
process_event_history(test_dict, customers)
# Check the bill has been computed correctly
bill = customers[0].generate_bill(1, 2018)
assert bill[0] == 5555
assert bill[1] == pytest.approx(-29.925)
assert bill[2][0]['total'] == pytest.approx(20)
assert bill[2][0]['free_mins'] == 1
assert bill[2][1]['total'] == pytest.approx(50.05)
assert bill[2][1]['billed_mins'] == 1
assert bill[2][2]['total'] == pytest.approx(-99.975)
assert bill[2][2]['billed_mins'] == 1
# Check the CallHistory objects are populated
history = customers[0].get_call_history('867-5309')
assert len(history) == 1
assert len(history[0].incoming_calls) == 1
assert len(history[0].outgoing_calls) == 1
history = customers[0].get_call_history()
assert len(history) == 3
assert len(history[0].incoming_calls) == 1
assert len(history[0].outgoing_calls) == 1
def test_contract_start_dates() -> None:
""" Test the start dates of the contracts.
Ensure that the start dates are the correct dates as specified in the given
starter code.
"""
customers = create_customers(test_dict)
for c in customers:
for pl in c._phone_lines:
assert pl.contract.start == datetime.date(year=2017, month=12, day=25)
if hasattr(pl.contract, 'end'): # only check if there is an end date (TermContract)
assert pl.contract.end == datetime.date(year=2019, month=6, day=25)
def test_filters() -> None:
""" Test the functionality of the filters.
We are only giving you a couple of tests here, you should expand both the
dataset and the tests for the different types of applicable filters
"""
customers = create_customers(test_dict)
process_event_history(test_dict, customers)
# Populate the list of calls:
calls = []
hist = customers[0].get_history()
# only consider outgoing calls, we don't want to duplicate calls in the test
calls.extend(hist[0])
# The different filters we are testing
filters = [
DurationFilter(),
CustomerFilter(),
ResetFilter()
]
# These are the inputs to each of the above filters in order.
# Each list is a test for this input to the filter
filter_strings = [
["L50", "G10", "L0", "50", "AA", ""],
["5555", "1111", "9999", "aaaaaaaa", ""],
["rrrr", ""]
]
# These are the expected outputs from the above filter application
# onto the full list of calls
expected_return_lengths = [
[1, 2, 0, 3, 3, 3],
[3, 3, 3, 3, 3],
[3, 3]
]
for i in range(len(filters)):
for j in range(len(filter_strings[i])):
result = filters[i].apply(customers, calls, filter_strings[i][j])
assert len(result) == expected_return_lengths[i][j]
if __name__ == '__main__':
pytest.main(['sample_tests.py'])
__MACOSX/starter_code-2/._sample_tests.py
starter_code-2/data.py
# This is formatted to reveal the structure of the JSON data files.
tiny_data = \
{"events": [
{"type": "sms",
"src_number": "422-4785",
"dst_number": "731-0105",
"time": "2018-01-01 14:29:05",
"src_loc": [-79.42848154284123, 43.641401675960374],
"dst_loc": [-79.52745693913239, 43.750338501653374]},
{"type": "sms",
"src_number": "934-0592",
"dst_number": "136-5226",
"time": "2018-01-02 03:17:57",
"src_loc": [-79.45188229255568, 43.62186408875219],
"dst_loc": [-79.36866519485261, 43.680793196449336]},
{"type": "call",
"src_number": "422-4785",
"dst_number": "136-5226",
"time": "2018-01-03 02:14:31",
"duration": 117,
"src_loc": [-79.68079993411648, 43.64986163420895],
"dst_loc": [-79.46762523704258, 43.59568659654661]}
],
"customers": [
{"lines": [{"number": "861-1710", "contract": "mtm"}], "id": 2247},
{"lines": [{"number": "426-4804", "contract": "term"},
{"number": "934-0592", "contract": "term"},
{"number": "131-3768", "contract": "prepaid"},
{"number": "386-6346", "contract": "term"}], "id": 3895},
{"lines": [{"number": "931-8588", "contract": "term"},
{"number": "981-7145", "contract": "mtm"},
{"number": "817-5571", "contract": "mtm"},
{"number": "581-0057", "contract": "mtm"},
{"number": "452-7360", "contract": "prepaid"}], "id": 8548},
{"lines": [{"number": "895-2223", "contract": "prepaid"},
{"number": "425-5910", "contract": "mtm"},
{"number": "731-0105", "contract": "term"},
{"number": "136-5226", "contract": "mtm"},
{"number": "422-4785", "contract": "prepaid"}], "id": 5716},
{"lines": [{"number": "829-6467", "contract": "term"}], "id": 9701}
]
}
__MACOSX/starter_code-2/._data.py
starter_code-2/pyta_report.html

PyTA Error Report
Tue. Feb. 19 2019, 07:35:45 PM





/Users/maria/Desktop/csc148/assignments/a1/starter_code/callhistory.py



Code Errors or Forbidden Usage (fix: high priority)

Expand/Collapse Section




None!





Style or Convention Errors (fix: before submission)

Expand/Collapse Section







❐ C0301 (line-too-long)


2

occurrences.






Expand/Collapse






[Line 41] Line too long (85/80)


39 """ Register a Call into this outgoing call history
40 """
41 # TODO: Implement this method. # i have already done it not sure if its right
42 if self.outgoing_calls == {}:
43 self.outgoing_calls[call.get_bill_date()] = [call]





[Line 49] Line too long (85/80)


47 """ Register a Call into this incoming call history
48 """
49 # TODO: Implement this method. # i have already done it not sure if its right
50 if self.incoming_calls == {}:
51 self.incoming_calls[call.get_bill_date()] = [call]











Found a bug? Report it to Prof. Liu!









__MACOSX/starter_code-2/._pyta_report.html
starter_code-2/visualizer.py
"""
CSC148, Winter 2019
Assignment 1
This code is provided solely for the personal and private use of
students taking the CSC148 course at the University of Toronto.
Copying for purposes other than this use is expressly prohibited.
All forms of distribution of this code, whether as given or with
any changes, are expressly prohibited.
All of the files in this directory and all subdirectories are:
Copyright (c) 2019 Bogdan Simion, Diane Horton, Jacqueline Smith
"""
import os
import threading
import math
import time
from typing import List, Tuple, Any, Optional, Union, Callable
from tkinter import *
import pygame
from call import Drawable, Call
from customer import Customer
from filter import DurationFilter, CustomerFilter, LocationFilter, ResetFilter
"""
=== Module Description ===
This file contains the Visualizer class, which is responsible for interacting
with Pygame, the graphics library we're using for this assignment.
There's quite a bit in this file, but you aren't responsible for most of it.
It also contains the Map class, which is responsible for converting between
longitude/latitude coordinates and pixel coordinates on the pygame window.
DO NOT CHANGE ANY CODE IN THIS FILE, unless instructed in the handout.
"""
# ----------------------------------------------------------------------------
# NOTE: You do not need to understand any of the visualization details from
# this module, to be able to solve this assignment. However, feel free to read
# it for the fun of understanding the visualization system.
# ----------------------------------------------------------------------------
WHITE = (255, 255, 255)
LINE_COLOUR = (0, 64, 125)
MAP_FILE = 'data/toronto_map.png'
# Map upper-left and bottom-right coordinates (long, lat).
MAP_MIN = (-79.697878, 43.799568)
MAP_MAX = (-79.196382, 43.576959)
# Window size
SCREEN_SIZE = (1000, 700)
class Visualizer:
"""Visualizer for the current state of a simulation.
=== Public attributes ===
r: the Tk object for the main window
"""
# === Private attributes ===
# _screen: the pygame window that is shown to the user.
# _mouse_down: whether the user is holding down a mouse button
# on the pygame window.
# _map: the Map object responsible for converting between longitude/latitude
# coordinates and the pixels of the visualization window.
_uiscreen: pygame.Surface
_screen: pygame.Surface
_mouse_down: bool
_map: 'Map'
_quit: bool
r: Tk
def __init__(self) -> None:
"""Initialize this visualization.
"""
self.r = Tk()
Label(self.r, text="Welcome to MewbileTech phone management system")\
.grid(row=0, column=0)
self.r.title("MewbileTech management system")
pygame.init()
self._uiscreen = pygame.display.set_mode(
(SCREEN_SIZE[0] + 200, SCREEN_SIZE[1]),
pygame.HWSURFACE | pygame.DOUBLEBUF)
# Add the text along the side, displaying the command keys for filters
self._uiscreen.fill((125, 125, 125))
font = pygame.font.SysFont(None, 25)
self._uiscreen.blit(font.render("FILTER KEYBINDS", True, WHITE),
(SCREEN_SIZE[0] + 10, 50))
self._uiscreen.blit(font.render("C: customer ID", True, WHITE),
(SCREEN_SIZE[0] + 10, 100))
self._uiscreen.blit(font.render("D: duration", True, WHITE),
(SCREEN_SIZE[0] + 10, 150))
self._uiscreen.blit(font.render("L: location", True, WHITE),
(SCREEN_SIZE[0] + 10, 200))
self._uiscreen.blit(font.render("R: reset filter", True, WHITE),
(SCREEN_SIZE[0] + 10, 250))
self._uiscreen.blit(font.render("M: monthly bill", True, WHITE),
(SCREEN_SIZE[0] + 10, 650))
self._screen = self._uiscreen.subsurface((0, 0), SCREEN_SIZE)
self._screen.fill(WHITE)
self._mouse_down = False
self._map = Map(SCREEN_SIZE)
# Initial render
self.render_drawables([])
self._quit = False
def render_drawables(self, drawables: List[Drawable]) -> None:
"""Render the to the screen
"""
# Draw the background map onto the screen
self._screen.fill(WHITE)
self._screen.blit(self._map.get_current_view(), (0, 0))
# Add all of the objects onto the screen
self._map.render_objects(drawables, self._screen)
# Show the new image
pygame.display.flip()
def has_quit(self) -> bool:
"""Returns if the program has received the quit command
"""
return self._quit
def handle_window_events(self, customers: List[Customer],
drawables: List[Call]) \
-> List[Call]:
"""Handle any user events triggered through the pygame window.
The are the objects currently displayed, while the
list contains all customers from the input data.
Return a new list of Calls, according to user input actions.
"""
new_drawables = drawables
for event in pygame.event.get():
if event.type == pygame.QUIT:
self._quit = True
elif event.type == pygame.KEYDOWN:
f = None
num_threads = 1
if event.unicode == "d":
f = DurationFilter()
elif event.unicode == "l":
f = LocationFilter()
elif event.unicode == "c":
f = CustomerFilter()
elif event.unicode == "r":
f = ResetFilter()
num_threads = 1
if f is not None:
def result_wrapper(fun: Callable[[List[Customer],
List[Call],
str], List[Call]],
customers: List[Customer],
data: List[Call],
filter_string: str,
res: List) -> None:
"""A final wrapper to return the result of the operation
"""
res.append(fun(customers, data, filter_string))
def threading_wrapper(customers: List[Customer],
data: List[Call],
filter_string: str) -> List[Call]:
"""A wrapper for the application of filters with
threading
"""
chunk_sz_calls = math.ceil(
(len(data) + num_threads - 1) / num_threads)
print("Num_threads:", num_threads)
print("Chunk_calls:", chunk_sz_calls)
threads = []
results = []
for i in range(num_threads):
res = []
results.append(res)
t = threading.Thread(target=result_wrapper,
args=
(f.apply,
customers,
data[i*chunk_sz_calls:
(i+1)*chunk_sz_calls],
filter_string,
res))
t.daemon = True
t.start()
threads.append(t)
# f.apply(customers, data, filter_string)
# Wait to finish
for t in threads:
t.join()
# Now reconstruct the data
new_data = []
for res in results:
new_data.extend(res[0])
return new_data
new_drawables = self.entry_window(str(f),
customers,
drawables,
threading_wrapper)
# Perform the billing for a selected customer:
if event.unicode == "m":
try:
def get_customer(customers: List[Customer],
found_customer: List[Customer],
input_string: str) -> None:
""" A helper to find the customer specified in the
input string appends to the found_customer the
matching customer
"""
try:
for c in customers:
if c.get_id() == int(input_string):
found_customer.append(c)
except ValueError:
pass
customer = []
self.entry_window("Generate the bill for the customer "
"with ID:", customers, customer,
get_customer)
if len(customer) == 0:
raise ValueError
# Just want to return the parsed input string
def get_input_date(customer: List[Customer],
drawables: List[Call],
input_string: str) \
-> Optional[List[int]]:
""" A helper to get the input date """
try:
return [int(s.strip())
for s in input_string.split(',')]
except ValueError:
return None
date = self.entry_window("Bill month and year: "
"month, year",
customers,
drawables,
get_input_date)
if date is None or date == ([], []):
raise ValueError
customer[0].print_bill(date[0], date[1])
except ValueError:
print("ERROR: bad formatting for input string")
except IndexError:
print("Customer not found")
elif event.type == pygame.MOUSEBUTTONDOWN:
if event.button == 1:
self._mouse_down = True
elif event.button == 4:
self._map.zoom(-0.1)
elif event.button == 5:
self._map.zoom(0.1)
elif event.type == pygame.MOUSEBUTTONUP:
self._mouse_down = False
elif event.type == pygame.MOUSEMOTION:
if self._mouse_down:
self._map.pan(pygame.mouse.get_rel())
else:
pygame.mouse.get_rel()
return new_drawables
def entry_window(self, field: str,
customers: List[Customer],
drawables: Union[List[Customer],
List[Call]],
callback: Callable[[List[Customer],
List[Call],
str],
List[Call]]) \
-> Union[List[Call], List[Any]]:
""" Creates a pop-up window for the user to enter input text, and
applies the function onto the
"""
new_drawables = []
m = Tk()
m.title("Filter")
Label(m, text=field).grid(row=0)
el = Entry(m)
# No textbox for filter string if it's a Reset filter
if field != "Reset all of the filters applied so far, if any":
el.grid(row=0, column=1)
# The callback function:
def callback_wrapper(input_string: str) -> None:
""" A wrapper to call the callback function on the
and print the time taken for the function to execute.
"""
nonlocal new_drawables
nonlocal m
t1 = time.time()
new_drawables = callback(customers, drawables, input_string)
t2 = time.time()
print("Time elapsed: " + str(t2 - t1))
m.destroy()
Button(m, text="Apply Filter",
command=lambda:
callback_wrapper(el.get()
if field != "Reset all of the filters applied "
"so far, if any"
else "")).grid(row=1, column=0,
sticky=W, pady=5)
m.mainloop()
print("FILTER APPLIED")
return new_drawables
class Map:
""" Window panning and zooming interface.
=== Public attributes ===
image:
the full image for the area to cover with the map
min_coords:
the minimum long/lat coordinates
max_coords:
the maximum long/lat coordinates
screensize:
the dimensions of the screen
"""
# === Private attributes ===
# _xoffset:
# offset on x axis
# _yoffset:
# offset on y axis
# _zoom:
# map zoom level
image: pygame.image
min_coords: Tuple[float, float]
max_coords: Tuple[float, float]
screensize: Tuple[int, int]
_xoffset: int
_yoffset: int
_zoom: int
def __init__(self, screendims: Tuple[int, int]) -> None:
""" Initialize this map for the given screen dimensions .
"""
self.image = pygame.image.load(
os.path.join(os.path.dirname(__file__), MAP_FILE))
self.min_coords = MAP_MIN
self.max_coords = MAP_MAX
self._xoffset = 0
self._yoffset = 0
self._zoom = 1
self.screensize = screendims
def render_objects(self, drawables: List[Drawable],
screen: pygame.Surface) -> None:
""" Render the onto the .
"""
for drawable in drawables:
longlat_position = drawable.get_position()
if longlat_position is not None:
sprite_position = self._longlat_to_screen(longlat_position)
screen.blit(drawable.sprite, sprite_position)
else: # is a line segment
endpoints = drawable.get_linelimits()
pygame.draw.aaline(screen,
LINE_COLOUR,
self._longlat_to_screen(endpoints[0]),
self._longlat_to_screen(endpoints[1]))
def _longlat_to_screen(self,
location: Tuple[float, float]) -> Tuple[int, int]:
""" Convert the long/lat coordinates into pixel coordinates.
"""
x = round((location[0] - self.min_coords[0]) /
(self.max_coords[0] - self.min_coords[0]) *
self.image.get_width())
y = round((location[1] - self.min_coords[1]) /
(self.max_coords[1] - self.min_coords[1]) *
self.image.get_height())
x = round((x - self._xoffset) * self._zoom * self.screensize[0] /
self.image.get_width())
y = round((y - self._yoffset) * self._zoom * self.screensize[1] /
self.image.get_height())
return x, y
def pan(self, dp: Tuple[int, int]) -> None:
""" Pan the view in the image by (dx, dy) screenspace pixels.
"""
self._xoffset -= dp[0]
self._yoffset -= dp[1]
self._clamp_transformation()
def zoom(self, dx: float) -> None:
""" Zoom the view by amount.
The centre of the zoom is the top-left corner of the visible region.
"""
if (self._zoom >= 4 and dx > 0) or (self._zoom <= 1 and dx < 0):
return
self._zoom += dx
self._clamp_transformation()
def _clamp_transformation(self) -> None:
""" Ensure that the transformation parameters are within a fixed range.
"""
raw_width = self.image.get_width()
raw_height = self.image.get_height()
zoom_width = round(raw_width / self._zoom)
zoom_height = round(raw_height / self._zoom)
self._xoffset = min(raw_width - zoom_width, max(0, self._xoffset))
self._yoffset = min(raw_height - zoom_height, max(0, self._yoffset))
def get_current_view(self) -> pygame.Surface:
""" Get the subimage to display to screen from the map.
"""
raw_width = self.image.get_width()
raw_height = self.image.get_height()
zoom_width = round(raw_width / self._zoom)
zoom_height = round(raw_height / self._zoom)
mapsegment = self.image.subsurface(((self._xoffset, self._yoffset),
(zoom_width, zoom_height)))
return pygame.transform.smoothscale(mapsegment, self.screensize)
if __name__ == '__main__':
import python_ta
python_ta.check_all(config={
'allowed-import-modules': [
'doctest', 'python_ta', 'typing',
'tkinter', 'os', 'pygame',
'threading', 'math', 'time',
'customer', 'call', 'filter',
],
'allowed-io': [
'entry_window', 'callback_wrapper', 'threading_wrapper',
'__init__', 'handle_window_events'
],
'disable': ['R0915', 'W0613', 'W0401', 'R0201'],
'generated-members': 'pygame.*'
})
__MACOSX/starter_code-2/._visualizer.py
starter_code-2/dataset.json
{"events": [{"type": "sms", "src_number": "776-2120", "dst_number": "839-0275", "time": "2018-01-01 09:44:14", "src_loc": [-79.67406228655243, 43.70727892550828], "dst_loc": [-79.30498277210351, 43.64927878254314]}, {"type": "call", "src_number": "938-6680", "dst_number": "674-6199", "time": "2018-01-01 11:05:55", "duration": 305, "src_loc": [-79.49877597785999, 43.60212042242521], "dst_loc": [-79.44914130508965, 43.758957913726896]}, {"type": "sms", "src_number": "247-2510", "dst_number": "698-0146", "time": "2018-01-01 13:56:34", "src_loc": [-79.63088166540733, 43.62982612424343], "dst_loc": [-79.3145961533051, 43.7555356005157]}, {"type": "sms", "src_number": "165-7838", "dst_number": "628-1219", "time": "2018-01-01 20:10:56", "src_loc": [-79.41712961780111, 43.69775878465157], "dst_loc": [-79.52022463020653, 43.667528298933334]}, {"type": "call", "src_number": "056-7577", "dst_number": "285-3740", "time": "2018-01-02 02:03:15", "duration": 163, "src_loc": [-79.24422244488586, 43.77413520361959], "dst_loc": [-79.33181640187328, 43.75083004709019]}, {"type": "sms", "src_number": "334-0547", "dst_number": "611-2902", "time": "2018-01-02 03:03:53", "src_loc": [-79.41414510099095, 43.63591498100487], "dst_loc": [-79.43253057464823, 43.73169178852711]}, {"type": "call", "src_number": "496-5543", "dst_number": "871-6653", "time": "2018-01-02 07:56:20", "duration": 321, "src_loc": [-79.69342977888867, 43.57804035120256], "dst_loc": [-79.26141442215547, 43.70123217109055]}, {"type": "call", "src_number": "050-0105", "dst_number": "696-2653", "time": "2018-01-02 12:32:46", "duration": 133, "src_loc": [-79.54436752829307, 43.73929365615721], "dst_loc": [-79.44050232585877, 43.66062108305516]}, {"type": "call", "src_number": "832-2301", "dst_number": "824-8496", "time": "2018-01-02 14:41:07", "duration": 324, "src_loc": [-79.60802878991359, 43.693351585074346], "dst_loc": [-79.35293268943096, 43.79505647636809]}, {"type": "sms", "src_number": "913-2332", "dst_number": "730-7684", "time": "2018-01-02 16:42:53", "src_loc": [-79.46541959701995, 43.7606014175198], "dst_loc": [-79.5389825061505, 43.62726017734569]}, {"type": "sms", "src_number": "843-6834", "dst_number": "993-4723", "time": "2018-01-02 23:20:06", "src_loc": [-79.67355734784488, 43.600399301141934], "dst_loc": [-79.57812839679308, 43.60258260418795]}, {"type": "call", "src_number": "824-8496", "dst_number": "835-1248", "time": "2018-01-03 00:46:34", "duration": 196, "src_loc": [-79.38870227128515, 43.75778630255226], "dst_loc": [-79.20266410303202, 43.65216911152068]}, {"type": "sms", "src_number": "277-0239", "dst_number": "373-3350", "time": "2018-01-03 02:33:25", "src_loc": [-79.3181487698864, 43.71893579760639], "dst_loc": [-79.63202262701112, 43.797600288753074]}, {"type": "sms", "src_number": "575-6924", "dst_number": "533-1214", "time": "2018-01-03 04:06:00", "src_loc": [-79.35931926326565, 43.739308356397046], "dst_loc": [-79.46866717947026, 43.74273886378449]}, {"type": "sms", "src_number": "649-0500", "dst_number": "006-3878", "time": "2018-01-03 06:01:55", "src_loc": [-79.3282737094879, 43.68513147513064], "dst_loc": [-79.6815448350069, 43.63645839756315]}, {"type": "sms", "src_number": "059-4057", "dst_number": "102-9506", "time": "2018-01-03 07:29:54", "src_loc": [-79.56713264587303, 43.73831227476857], "dst_loc": [-79.39055307175315, 43.633440454325]}, {"type": "call", "src_number": "100-8771", "dst_number": "463-0109", "time": "2018-01-03 09:30:28", "duration": 322, "src_loc": [-79.64766801685782, 43.63849731441832], "dst_loc": [-79.4033654340801, 43.64676624965331]}, {"type": "sms", "src_number": "987-2747", "dst_number": "241-0963", "time": "2018-01-03 12:32:04", "src_loc": [-79.27688286733527, 43.678029880470824], "dst_loc": [-79.6025473101743, 43.770289240259125]}, {"type": "call", "src_number": "862-3646", "dst_number": "628-1219", "time": "2018-01-03 16:22:33", "duration": 270, "src_loc": [-79.2019388259489, 43.730170367672045], "dst_loc": [-79.62013525898622, 43.705310955370436]}, {"type": "call", "src_number": "006-3878", "dst_number": "696-2653", "time": "2018-01-03 18:12:19", "duration": 284, "src_loc": [-79.53197307243349, 43.634042426127294], "dst_loc": [-79.63208934720531, 43.58749975924126]}, {"type": "call", "src_number": "300-7295", "dst_number": "730-5600", "time": "2018-01-04 00:04:27", "duration": 81, "src_loc": [-79.4537984572693, 43.691041754578535], "dst_loc": [-79.33435457922519, 43.707046709462276]}, {"type": "call", "src_number": "938-6680", "dst_number": "730-5600", "time": "2018-01-04 14:58:07", "duration": 301, "src_loc": [-79.54717029563304, 43.58020061333404], "dst_loc": [-79.31511855020865, 43.61495646767958]}, {"type": "call", "src_number": "824-8496", "dst_number": "373-3350", "time": "2018-01-04 17:00:00", "duration": 203, "src_loc": [-79.26403584840118, 43.658578064986294], "dst_loc": [-79.27768087021423, 43.68802302935565]}, {"type": "sms", "src_number": "668-3514", "dst_number": "142-5525", "time": "2018-01-05 03:13:57", "src_loc": [-79.53282687724422, 43.67389313968766], "dst_loc": [-79.37451392155718, 43.61493258584348]}, {"type": "call", "src_number": "936-4231", "dst_number": "839-6337", "time": "2018-01-05 04:48:00", "duration": 91, "src_loc": [-79.2079781340112, 43.65767041901683], "dst_loc": [-79.51119008281646, 43.666849635058625]}, {"type": "call", "src_number": "129-8541", "dst_number": "990-0629", "time": "2018-01-05 07:04:06", "duration": 264, "src_loc": [-79.49230222546187, 43.7598826258876], "dst_loc": [-79.55777861385711, 43.66121709273177]}, {"type": "sms", "src_number": "940-6196", "dst_number": "592-4066", "time": "2018-01-05 08:00:35", "src_loc": [-79.25392315218693, 43.693331621099155], "dst_loc": [-79.47674995597465, 43.73962032872]}, {"type": "call", "src_number": "533-1214", "dst_number": "731-6163", "time": "2018-01-05 12:29:15", "duration": 61, "src_loc": [-79.54909954357234, 43.655887932477825], "dst_loc": [-79.2743070643613, 43.6402452783943]}, {"type": "sms", "src_number": "520-2727", "dst_number": "637-0682", "time": "2018-01-05 14:41:32", "src_loc": [-79.57158819958472, 43.693715588084515], "dst_loc": [-79.68457846725991, 43.67640335129593]}, {"type": "sms", "src_number": "862-3646", "dst_number": "401-3187", "time": "2018-01-06 00:42:27", "src_loc": [-79.38892969466735, 43.73580596531158], "dst_loc": [-79.66346829510047, 43.643430470205665]}, {"type": "call", "src_number": "597-9070", "dst_number": "956-4003", "time": "2018-01-06 01:02:47", "duration": 182, "src_loc": [-79.60104741898402, 43.73060458103853], "dst_loc": [-79.2841268202547, 43.71350412475424]}, {"type": "call", "src_number": "006-3878", "dst_number": "418-9185", "time": "2018-01-06 01:15:01", "duration": 38, "src_loc": [-79.40770521600528, 43.60467018173296], "dst_loc": [-79.41510421157959, 43.578944502397114]}, {"type": "sms", "src_number": "708-6848", "dst_number": "198-0536", "time": "2018-01-06 08:46:52", "src_loc": [-79.25963186530988, 43.72758907038445], "dst_loc": [-79.41500642216496, 43.73491414012541]}, {"type": "sms", "src_number": "293-2025", "dst_number": "444-0066", "time": "2018-01-06 16:22:27", "src_loc": [-79.35954688608511, 43.62749381216327], "dst_loc": [-79.27471945814955, 43.67530407666513]}, {"type": "sms", "src_number": "161-4158", "dst_number": "430-4508", "time": "2018-01-06 17:17:03", "src_loc": [-79.58251144846474, 43.623510097350334], "dst_loc": [-79.37271957679349, 43.64182998071287]}, {"type": "sms", "src_number": "112-9752", "dst_number": "003-3751", "time": "2018-01-06 18:01:58", "src_loc": [-79.51594858829141, 43.74806303482998], "dst_loc": [-79.5902220744822, 43.5831899926382]}, {"type": "sms", "src_number": "444-0066", "dst_number": "412-2420", "time": "2018-01-06 19:17:09", "src_loc": [-79.22921675401936, 43.62355414852692], "dst_loc": [-79.21983513415361, 43.60396140453009]}, {"type": "call", "src_number": "300-7295", "dst_number": "784-0925", "time": "2018-01-07 01:38:47", "duration": 109, "src_loc": [-79.24245969002672, 43.624241859334795], "dst_loc": [-79.32254158434391, 43.612534143365274]}, {"type": "sms", "src_number": "607-9319", "dst_number": "936-4231", "time": "2018-01-07 01:45:21", "src_loc": [-79.42454324147307, 43.656164866486684], "dst_loc": [-79.64913030521377, 43.63072126621635]}, {"type": "sms", "src_number": "099-1900", "dst_number": "444-0066", "time": "2018-01-07 03:45:24", "src_loc": [-79.69252497456388, 43.65748220493364], "dst_loc": [-79.29509830086768, 43.597999423618695]}, {"type": "sms", "src_number": "346-8644", "dst_number": "129-8541", "time": "2018-01-07 06:06:02", "src_loc": [-79.62796709816153, 43.68094046877313], "dst_loc": [-79.20513377270564, 43.79653299161143]}, {"type": "call", "src_number": "731-6514", "dst_number": "581-0237", "time": "2018-01-07 07:34:22", "duration": 18, "src_loc": [-79.52261420222837, 43.65700815276033], "dst_loc": [-79.38316975122211, 43.680925245805994]}, {"type": "sms", "src_number": "654-6297", "dst_number": "463-0109", "time": "2018-01-07 08:18:02", "src_loc": [-79.51126244706258, 43.578066714158744], "dst_loc": [-79.63306608596459, 43.7550766914385]}, {"type": "call", "src_number": "987-2747", "dst_number": "820-8986", "time": "2018-01-07 11:32:26", "duration": 14, "src_loc": [-79.21105133798072, 43.58582395068167], "dst_loc": [-79.30821272364325, 43.6916616599624]}, {"type": "sms", "src_number": "459-5273", "dst_number": "971-4536", "time": "2018-01-07 12:26:44", "src_loc": [-79.29733598792862, 43.57865371478416], "dst_loc": [-79.46761132863476, 43.69377783985488]}, {"type": "call", "src_number": "592-4066", "dst_number": "871-6653", "time": "2018-01-07 14:46:45", "duration": 248, "src_loc": [-79.50007316628995, 43.58161586716922], "dst_loc": [-79.49591258461501, 43.69964953140681]}, {"type": "sms", "src_number": "048-4695", "dst_number": "839-0038", "time": "2018-01-07 21:13:34", "src_loc": [-79.51378529280056, 43.67456962575426], "dst_loc": [-79.59232021902177, 43.6991330136348]}, {"type": "call", "src_number": "493-8650", "dst_number": "871-6653", "time": "2018-01-08 01:32:53", "duration": 332, "src_loc": [-79.20366626407794, 43.66536478057691], "dst_loc": [-79.26724152151735, 43.644154349331636]}, {"type": "sms", "src_number": "665-2075", "dst_number": "373-3350", "time": "2018-01-08 02:07:01", "src_loc": [-79.63637744679589, 43.742984196738135], "dst_loc": [-79.3159948243952, 43.69808380847264]}, {"type": "call", "src_number": "010-3671", "dst_number": "003-3751", "time": "2018-01-08 13:50:47", "duration": 228, "src_loc": [-79.65427688179467, 43.57970398268384], "dst_loc": [-79.52734620498566, 43.755670347103724]}, {"type": "sms", "src_number": "839-7840", "dst_number": "459-5273", "time": "2018-01-08 20:08:40", "src_loc": [-79.25043267097325, 43.68505870056715], "dst_loc": [-79.5986005641333, 43.70960647387574]}, {"type": "sms", "src_number": "328-2096", "dst_number": "444-0066", "time": "2018-01-08 20:32:20", "src_loc": [-79.37755825294823, 43.765960425597], "dst_loc": [-79.31760574502015, 43.6865498795319]}, {"type": "sms", "src_number": "285-3740", "dst_number": "998-1883", "time": "2018-01-08 21:31:40", "src_loc": [-79.48779210073988, 43.68643052724031], "dst_loc": [-79.4990521076639, 43.649126738766235]}, {"type": "sms", "src_number": "059-4057", "dst_number": "247-2510", "time": "2018-01-09 04:13:09", "src_loc": [-79.44057879170818, 43.600037365767896], "dst_loc": [-79.30513308139848, 43.577887836132135]}, {"type": "call", "src_number": "871-6653", "dst_number": "412-2420", "time": "2018-01-09 06:57:57", "duration": 154, "src_loc": [-79.62724786220454, 43.70453558610335], "dst_loc": [-79.39350202921781, 43.71646296027874]}, {"type": "sms", "src_number": "370-6462", "dst_number": "971-4536", "time": "2018-01-09 10:03:44", "src_loc": [-79.55707164720829, 43.663608302052864], "dst_loc": [-79.29352473000124, 43.68970508957421]}, {"type": "sms", "src_number": "731-6163", "dst_number": "839-7840", "time": "2018-01-09 12:16:23", "src_loc": [-79.60427630727446, 43.646057597055005], "dst_loc": [-79.6076341842898, 43.70426850147826]}, {"type": "call", "src_number": "698-0146", "dst_number": "380-5011", "time": "2018-01-09 15:28:38", "duration": 272, "src_loc": [-79.37075747653459, 43.78722242542597], "dst_loc": [-79.4049659085542, 43.598093728867816]}, {"type": "call", "src_number": "183-7942", "dst_number": "668-3514", "time": "2018-01-09 15:52:19", "duration": 89, "src_loc": [-79.37551083202906, 43.67031392343567], "dst_loc": [-79.67687278459942, 43.62175915693354]}, {"type": "sms", "src_number": "914-9837", "dst_number": "013-3456", "time": "2018-01-09 17:48:49", "src_loc": [-79.37969442159617, 43.714408997628546], "dst_loc": [-79.52981031587542, 43.604439071034996]}, {"type": "call", "src_number": "380-5011", "dst_number": "961-1759", "time": "2018-01-09 18:05:30", "duration": 288, "src_loc": [-79.52793641616302, 43.697734691366726], "dst_loc": [-79.68402823093548, 43.726466393302374]}, {"type": "sms", "src_number": "832-2301", "dst_number": "142-5525", "time": "2018-01-09 18:49:19", "src_loc": [-79.61227444370269, 43.69280548896062], "dst_loc": [-79.3424980486224, 43.79691389139773]}, {"type": "sms", "src_number": "730-5600", "dst_number": "178-3378", "time": "2018-01-09 22:12:36", "src_loc": [-79.37565439771913, 43.763503198552], "dst_loc": [-79.54883514551562, 43.647158263085565]}, {"type": "sms", "src_number": "649-0500", "dst_number": "628-9627", "time": "2018-01-10 02:47:34", "src_loc": [-79.51600932571661, 43.65717441472133], "dst_loc": [-79.4694826645782, 43.65960012473842]}, {"type": "call", "src_number": "059-4057", "dst_number": "987-2747", "time": "2018-01-10 07:12:38", "duration": 40, "src_loc": [-79.36857336678734, 43.73480181057429], "dst_loc": [-79.60595661356118, 43.65961376277408]}, {"type": "call", "src_number": "676-5324", "dst_number": "831-3310", "time": "2018-01-10 17:01:26", "duration": 119, "src_loc": [-79.34562443941661, 43.774545620665045], "dst_loc": [-79.58286717813802, 43.78636190888336]}, {"type": "call", "src_number": "961-1759", "dst_number": "129-8541", "time": "2018-01-10 19:22:57", "duration": 129, "src_loc": [-79.37699057963226, 43.77694267438579], "dst_loc": [-79.24968025585535, 43.69845982010377]}, {"type": "sms", "src_number": "806-5506", "dst_number": "261-2835", "time": "2018-01-10 20:43:08", "src_loc": [-79.54064011866342, 43.63965266673999], "dst_loc": [-79.60047757431803, 43.72603768110868]}, {"type": "sms", "src_number": "644-8064", "dst_number": "835-1248", "time": "2018-01-10 21:46:30", "src_loc": [-79.62739253901992, 43.67336570021128], "dst_loc": [-79.5501391426594, 43.72706431004959]}, {"type": "call", "src_number": "961-1759", "dst_number": "932-2436", "time": "2018-01-11 00:28:19", "duration": 255, "src_loc": [-79.40329718847185, 43.61360933656805], "dst_loc": [-79.40426860556673, 43.70035519428543]}, {"type": "sms", "src_number": "198-0536", "dst_number": "784-0925", "time": "2018-01-11 02:21:47", "src_loc": [-79.26276830576765, 43.62819967483527], "dst_loc": [-79.5402673278375, 43.64368671247442]}, {"type": "call", "src_number": "006-3878", "dst_number": "430-4508", "time": "2018-01-11 02:35:23", "duration": 300, "src_loc": [-79.50570739400892, 43.69513525962271], "dst_loc": [-79.6957955807145, 43.73255472981517]}, {"type": "call", "src_number": "101-7414", "dst_number": "293-2025", "time": "2018-01-11 03:57:20", "duration": 196, "src_loc": [-79.5237417064168, 43.773790522197146], "dst_loc": [-79.52987163327002, 43.713236221446635]}, {"type": "call", "src_number": "845-3937", "dst_number": "603-0289", "time": "2018-01-11 05:11:41", "duration": 322, "src_loc": [-79.6042875786048, 43.794908835897786], "dst_loc": [-79.36796784921367, 43.79540845321941]}, {"type": "call", "src_number": "286-6787", "dst_number": "730-5600", "time": "2018-01-11 10:26:41", "duration": 96, "src_loc": [-79.67387618582906, 43.66802106708983], "dst_loc": [-79.5732118275938, 43.59958686353122]}, {"type": "sms", "src_number": "681-8883", "dst_number": "542-8018", "time": "2018-01-11 14:51:10", "src_loc": [-79.59075622772212, 43.63010785722206], "dst_loc": [-79.4545396570001, 43.655960370329524]}, {"type": "sms", "src_number": "542-8018", "dst_number": "444-0066", "time": "2018-01-11 18:21:59", "src_loc": [-79.40885229812663, 43.792531327178736], "dst_loc": [-79.48527777237571, 43.59605169737585]}, {"type": "call", "src_number": "611-2902", "dst_number": "580-3656", "time": "2018-01-11 20:29:08", "duration": 350, "src_loc": [-79.30573062564999, 43.59390038939616], "dst_loc": [-79.62982088743715, 43.795027485627]}, {"type": "call", "src_number": "695-4637", "dst_number": "126-2700", "time": "2018-01-11 22:24:28", "duration": 213, "src_loc": [-79.54108499598492, 43.69469635180983], "dst_loc": [-79.45806319837901, 43.61028937295674]}, {"type": "call", "src_number": "112-9752", "dst_number": "329-4692", "time": "2018-01-12 00:19:15", "duration": 47, "src_loc": [-79.6822302384206, 43.57806716581824], "dst_loc": [-79.44095721158409, 43.73520426126865]}, {"type": "sms", "src_number": "617-3262", "dst_number": "820-8986", "time": "2018-01-12 00:52:19", "src_loc": [-79.52237801982035, 43.68181178337716], "dst_loc": [-79.51952960594825, 43.77240269203005]}, {"type": "sms", "src_number": "832-2301", "dst_number": "010-3671", "time": "2018-01-12 01:06:00", "src_loc": [-79.60545036217661, 43.643797117284905], "dst_loc": [-79.58936690704283, 43.66334254160876]}, {"type": "sms", "src_number": "142-5525", "dst_number": "956-4003", "time": "2018-01-12 05:33:42", "src_loc": [-79.47022895101352, 43.68843384296585], "dst_loc": [-79.27007827374386, 43.74541285988904]}, {"type": "sms", "src_number": "338-1977", "dst_number": "497-8688", "time": "2018-01-12 11:12:15", "src_loc": [-79.32110153277249, 43.66574246932072], "dst_loc": [-79.54908053532826, 43.66711804596395]}, {"type": "call", "src_number": "947-1649", "dst_number": "617-3262", "time": "2018-01-12 12:34:04", "duration": 17, "src_loc": [-79.23725224853939, 43.59276512017153], "dst_loc": [-79.26719791410392, 43.722073437287776]}, {"type": "sms", "src_number": "047-5142", "dst_number": "009-8583", "time": "2018-01-12 14:20:33", "src_loc": [-79.58094942373967, 43.66699149561858], "dst_loc": [-79.5734873684463, 43.75786532167284]}, {"type": "call", "src_number": "300-7295", "dst_number": "575-6924", "time": "2018-01-13 01:08:08", "duration": 334, "src_loc": [-79.59168885583071, 43.733144198544075], "dst_loc": [-79.58806259186143, 43.75706334224579]}, {"type": "call", "src_number": "649-0500", "dst_number": "180-2368", "time": "2018-01-13 02:09:01", "duration": 263, "src_loc": [-79.26013513625395, 43.617553505502414], "dst_loc": [-79.41540905785176, 43.759040609061095]}, {"type": "call", "src_number": "971-4536", "dst_number": "936-4231", "time": "2018-01-13 03:02:57", "duration": 200, "src_loc": [-79.26851816661724, 43.60610611231158], "dst_loc": [-79.69092799600601, 43.73696567199281]}, {"type": "sms", "src_number": "542-8018", "dst_number": "006-3878", "time": "2018-01-13 07:02:20", "src_loc": [-79.54627294614588, 43.699018750414346], "dst_loc": [-79.47010997781422, 43.671233279610234]}, {"type": "call", "src_number": "413-0551", "dst_number": "637-0682", "time": "2018-01-13 08:30:20", "duration": 283, "src_loc": [-79.33852331254006, 43.61379305529684], "dst_loc": [-79.30690435736234, 43.585075707243206]}, {"type": "sms", "src_number": "537-6361", "dst_number": "407-3414", "time": "2018-01-13 08:33:59", "src_loc": [-79.34794843131971, 43.68615839957169], "dst_loc": [-79.31706064916634, 43.69922281121394]}, {"type": "call", "src_number": "784-0925", "dst_number": "412-2420", "time": "2018-01-13 12:54:34", "duration": 190, "src_loc": [-79.63184751621428, 43.69838974089598], "dst_loc": [-79.25135799796573, 43.71582998918007]}, {"type": "sms", "src_number": "542-8018", "dst_number": "784-0925", "time": "2018-01-13 14:39:33", "src_loc": [-79.3001838056724, 43.78184759605976], "dst_loc": [-79.3058740512167, 43.724055171596035]}, {"type": "call", "src_number": "277-0239", "dst_number": "413-0551", "time": "2018-01-13 18:51:21", "duration": 245, "src_loc": [-79.62014708033429, 43.68900200701229], "dst_loc": [-79.5761252838882, 43.787104535193855]}, {"type": "call", "src_number": "101-7414", "dst_number": "119-8468", "time": "2018-01-13 19:53:43", "duration": 79, "src_loc": [-79.42783105388449, 43.58392145820992], "dst_loc": [-79.43672458438752, 43.79477088820857]}, {"type": "call", "src_number": "730-5600", "dst_number": "137-5770", "time": "2018-01-13 21:00:25", "duration": 190, "src_loc": [-79.23022287306664, 43.752718747925314], "dst_loc": [-79.49598567590151, 43.58134030116757]}, {"type": "call", "src_number": "661-9241", "dst_number": "137-0722", "time": "2018-01-14 01:01:58", "duration": 36, "src_loc": [-79.22275477301001, 43.58878211375385], "dst_loc": [-79.40446032217965, 43.61274531378145]}, {"type": "call", "src_number": "165-7838", "dst_number": "482-2360", "time": "2018-01-14 01:11:04", "duration": 314, "src_loc": [-79.59710261937656, 43.70399012549619], "dst_loc": [-79.43485522850881, 43.79528830317918]}, {"type": "sms", "src_number": "493-8650", "dst_number": "784-0925", "time": "2018-01-14 10:29:20", "src_loc": [-79.53730433892723, 43.61614830645385], "dst_loc": [-79.2578531165587, 43.599751259231894]}, {"type": "sms", "src_number": "493-8650", "dst_number": "603-0289", "time": "2018-01-14 12:25:13", "src_loc": [-79.47478704177375, 43.76534642729039], "dst_loc": [-79.37228669267405, 43.59719072320908]}, {"type": "sms", "src_number": "801-9566", "dst_number": "286-6787", "time": "2018-01-14 14:10:48", "src_loc": [-79.54025477621796, 43.73504053102937], "dst_loc": [-79.3948410633355, 43.57928486946592]}, {"type": "sms", "src_number": "010-3671", "dst_number": "654-6297", "time": "2018-01-14 20:41:24", "src_loc": [-79.26634957095996, 43.6673833990915], "dst_loc": [-79.38727241150197, 43.765770480861214]}, {"type": "call", "src_number": "059-4057", "dst_number": "142-5525", "time": "2018-01-14 22:38:32", "duration": 311, "src_loc": [-79.65093107624631, 43.742354343872016], "dst_loc": [-79.52409090067196, 43.659690660221464]}, {"type": "call", "src_number": "843-6834", "dst_number": "576-9648", "time": "2018-01-15 02:00:44", "duration": 304, "src_loc": [-79.41860687371602, 43.726319641791555], "dst_loc": [-79.21348108399486, 43.609590629689805]}, {"type": "call", "src_number": "009-8583", "dst_number": "676-5324", "time": "2018-01-15 07:04:57", "duration": 335, "src_loc": [-79.50910557481441, 43.698810006013936], "dst_loc": [-79.58799242757146, 43.766298067512565]}, {"type": "sms", "src_number": "047-5142", "dst_number": "142-5525", "time": "2018-01-15 13:25:08", "src_loc": [-79.3289996774918, 43.735161486617834], "dst_loc": [-79.58742551471859, 43.794181490202995]}, {"type": "sms", "src_number": "286-6787", "dst_number": "119-8468", "time": "2018-01-15 14:27:35", "src_loc": [-79.28923167308983, 43.705773149140285], "dst_loc": [-79.45128378425497, 43.74262246431038]}, {"type": "call", "src_number": "849-3472", "dst_number": "401-3187", "time": "2018-01-15 19:26:33", "duration": 284, "src_loc": [-79.4826101670566, 43.7412367520351], "dst_loc": [-79.3202545626825, 43.77494722170838]}, {"type": "call", "src_number": "390-1713", "dst_number": "178-3378", "time": "2018-01-15 20:55:02", "duration": 180, "src_loc": [-79.35928604230101, 43.76099508684154], "dst_loc": [-79.33418362050143, 43.78644723802974]}, {"type": "call", "src_number": "801-9566", "dst_number": "730-7684", "time": "2018-01-15 22:33:56", "duration": 193, "src_loc": [-79.4727959464315, 43.6944882801766], "dst_loc": [-79.27329211166108, 43.68814488774526]}, {"type": "sms", "src_number": "696-4364", "dst_number": "744-6324", "time": "2018-01-16 02:40:21", "src_loc": [-79.23041633264242, 43.60907122027134], "dst_loc": [-79.43500460152787, 43.60874118667851]}, {"type": "call", "src_number": "580-3656", "dst_number": "990-0629", "time": "2018-01-16 02:48:28", "duration": 269, "src_loc": [-79.66565956856223, 43.608203662146174], "dst_loc": [-79.69637886600961, 43.614687433560746]}, {"type": "sms", "src_number": "644-8064", "dst_number": "560-7837", "time": "2018-01-16 08:35:06", "src_loc": [-79.53331811341755, 43.78325905715772], "dst_loc": [-79.64664072290546, 43.65634771421108]}, {"type": "sms", "src_number": "696-2653", "dst_number": "202-2830", "time": "2018-01-16 08:45:47", "src_loc": [-79.49070605903894, 43.68898797636147], "dst_loc": [-79.37524626937795, 43.78661849682256]}, {"type": "call", "src_number": "993-3441", "dst_number": "286-6787", "time": "2018-01-16 10:14:33", "duration": 248, "src_loc": [-79.24558750611436, 43.68905628506354], "dst_loc": [-79.51989562718796, 43.68505177064083]}, {"type": "sms", "src_number": "914-9837", "dst_number": "497-8688", "time": "2018-01-16 14:32:39", "src_loc": [-79.29098596410503, 43.67739481567266], "dst_loc": [-79.61131527948835, 43.69315058041223]}, {"type": "call", "src_number": "696-2653", "dst_number": "835-1248", "time": "2018-01-16 21:29:53", "duration": 220, "src_loc": [-79.27313388003954, 43.60438077438255], "dst_loc": [-79.23281137779779, 43.79673206013184]}, {"type": "call", "src_number": "277-0239", "dst_number": "849-3472", "time": "2018-01-16 22:19:38", "duration": 293, "src_loc": [-79.26515011489631, 43.78510909838326], "dst_loc": [-79.49487950951973, 43.65485367435405]}, {"type": "sms", "src_number": "137-0722", "dst_number": "006-3878", "time": "2018-01-16 22:39:24", "src_loc": [-79.37131360385959, 43.76687286644475], "dst_loc": [-79.50925410374074, 43.74051400895158]}, {"type": "sms", "src_number": "198-0536", "dst_number": "644-8064", "time": "2018-01-17 03:35:52", "src_loc": [-79.4853497238169, 43.741749475216835], "dst_loc": [-79.56999884477226, 43.60845801219503]}, {"type": "call", "src_number": "349-5337", "dst_number": "059-4057", "time": "2018-01-17 03:59:31", "duration": 250, "src_loc": [-79.4384913395394, 43.62102134374666], "dst_loc": [-79.39100653418305, 43.6398670600159]}, {"type": "call", "src_number": "603-0289", "dst_number": "329-4692", "time": "2018-01-17 04:04:43", "duration": 300, "src_loc": [-79.35254057558588, 43.758311561451585], "dst_loc": [-79.52989763763702, 43.696511356453776]}, {"type": "sms", "src_number": "607-9319", "dst_number": "815-0882", "time": "2018-01-17 07:34:45", "src_loc": [-79.47340316320667, 43.73141793137805], "dst_loc": [-79.50405630340731, 43.74144465367119]}, {"type": "sms", "src_number": "661-9241", "dst_number": "708-6848", "time": "2018-01-17 09:11:59", "src_loc": [-79.35528187654862, 43.72872359357342], "dst_loc": [-79.5371377810647, 43.67250941372903]}, {"type": "call", "src_number": "380-5011", "dst_number": "497-8688", "time": "2018-01-17 10:35:58", "duration": 321, "src_loc": [-79.55918204113351, 43.75892378493515], "dst_loc": [-79.3550537807653, 43.7524095791131]}, {"type": "call", "src_number": "971-4536", "dst_number": "731-6163", "time": "2018-01-17 12:06:55", "duration": 206, "src_loc": [-79.43411239505154, 43.75614318731803], "dst_loc": [-79.39487985681497, 43.72417025562037]}, {"type": "sms", "src_number": "629-6198", "dst_number": "649-0500", "time": "2018-01-17 15:00:45", "src_loc": [-79.37653588489238, 43.592538380304276], "dst_loc": [-79.66715055989128, 43.644437505781376]}, {"type": "call", "src_number": "334-0547", "dst_number": "681-8883", "time": "2018-01-17 19:57:54", "duration": 317, "src_loc": [-79.31815591565247, 43.7900184385362], "dst_loc": [-79.61046908792777, 43.69430284796392]}, {"type": "sms", "src_number": "947-1649", "dst_number": "993-4723", "time": "2018-01-17 21:03:31", "src_loc": [-79.40510011219709, 43.703730274607736], "dst_loc": [-79.61452113224658, 43.68968990271722]}, {"type": "call", "src_number": "101-7414", "dst_number": "731-6163", "time": "2018-01-18 06:33:45", "duration": 192, "src_loc": [-79.25035987428407, 43.783878712985455], "dst_loc": [-79.23097674862139, 43.61130495879614]}, {"type": "call", "src_number": "334-4295", "dst_number": "730-7684", "time": "2018-01-18 13:56:55", "duration": 249, "src_loc": [-79.43526915578126, 43.78054086590943], "dst_loc": [-79.23802419875206, 43.682403398300664]}, {"type": "call", "src_number": "592-4066", "dst_number": "261-2835", "time": "2018-01-18 15:14:03", "duration": 111, "src_loc": [-79.47187658708458, 43.684662024313866], "dst_loc": [-79.35290510679815, 43.59679843213539]}, {"type": "call", "src_number": "033-9988", "dst_number": "463-0109", "time": "2018-01-18 15:36:40", "duration": 187, "src_loc": [-79.43633588454476, 43.62400108942734], "dst_loc": [-79.58890427203471, 43.782319716595566]}, {"type": "sms", "src_number": "914-9837", "dst_number": "676-5324", "time": "2018-01-18 16:47:56", "src_loc": [-79.20707047452159, 43.70052466518845], "dst_loc": [-79.60253227518758, 43.69295452144067]}, {"type": "call", "src_number": "560-7837", "dst_number": "806-5506", "time": "2018-01-18 21:23:53", "duration": 143, "src_loc": [-79.4386263948658, 43.61388968666873], "dst_loc": [-79.56017290573554, 43.762188892148366]}, {"type": "call", "src_number": "346-8644", "dst_number": "009-8583", "time": "2018-01-18 22:08:03", "duration": 354, "src_loc": [-79.22661099584603, 43.628629192864956], "dst_loc": [-79.38817174413884, 43.712837046279446]}, {"type": "call", "src_number": "929-3363", "dst_number": "482-2360", "time": "2018-01-18 23:55:38", "duration": 90, "src_loc": [-79.65501642840135, 43.760633660957495], "dst_loc": [-79.63892322990388, 43.66404574606772]}, {"type": "call", "src_number": "644-8064", "dst_number": "159-1100", "time": "2018-01-19 05:40:17", "duration": 61, "src_loc": [-79.51944133147788, 43.60067681505971], "dst_loc": [-79.64288469835145, 43.58724739720348]}, {"type": "call", "src_number": "003-3751", "dst_number": "961-3055", "time": "2018-01-19 06:50:23", "duration": 218, "src_loc": [-79.57741542341567, 43.66439888968034], "dst_loc": [-79.38994237591073, 43.71080098517488]}, {"type": "sms", "src_number": "961-1759", "dst_number": "520-2727", "time": "2018-01-19 07:37:27", "src_loc": [-79.42000312897173, 43.65233976810541], "dst_loc": [-79.55895035101632, 43.593772027320036]}, {"type": "call", "src_number": "520-2727", "dst_number": "730-5600", "time": "2018-01-19 07:43:27", "duration": 65, "src_loc": [-79.28868112300222, 43.76826060895885], "dst_loc": [-79.64902961675654, 43.69802330590478]}, {"type": "call", "src_number": "668-3514", "dst_number": "102-9506", "time": "2018-01-19 08:00:26", "duration": 258, "src_loc": [-79.42906911359438, 43.66935735153906], "dst_loc": [-79.28336763563026, 43.66375323386153]}, {"type": "sms", "src_number": "099-1900", "dst_number": "533-1214", "time": "2018-01-19 08:42:42", "src_loc": [-79.53415434715518, 43.63312224424393], "dst_loc": [-79.4757702749552, 43.781540000295394]}, {"type": "call", "src_number": "349-5337", "dst_number": "099-1900", "time": "2018-01-19 09:37:27", "duration": 225, "src_loc": [-79.53490667723806, 43.63760429606875], "dst_loc": [-79.57324752613812, 43.7098345999575]}, {"type": "call", "src_number": "871-6653", "dst_number": "806-5506", "time": "2018-01-19 12:11:45", "duration": 156, "src_loc": [-79.59906242416758, 43.71543539174242], "dst_loc": [-79.65096568937967, 43.752101705634836]}, {"type": "call", "src_number": "938-6680", "dst_number": "430-4508", "time": "2018-01-19 14:51:32", "duration": 220, "src_loc": [-79.67621982395514, 43.707633517946036], "dst_loc": [-79.51866885116564, 43.702861005412906]}, {"type": "call", "src_number": "475-6203", "dst_number": "370-6462", "time": "2018-01-19 15:41:33", "duration": 259, "src_loc": [-79.58460373346657, 43.740385593213276], "dst_loc": [-79.5054393450373, 43.750448363296194]}, {"type": "call", "src_number": "047-5142", "dst_number": "277-0239", "time": "2018-01-19 15:52:30", "duration": 241, "src_loc": [-79.33596571946686, 43.61676146931326], "dst_loc": [-79.67450808784227, 43.75041590445127]}, {"type": "call", "src_number": "010-3671", "dst_number": "938-6680", "time": "2018-01-20 01:06:17", "duration": 348, "src_loc": [-79.3814038413827, 43.66098035684695], "dst_loc": [-79.60654159767151, 43.75978323027038]}, {"type": "sms", "src_number": "159-1100", "dst_number": "835-1248", "time": "2018-01-20 01:47:12", "src_loc": [-79.6123900837108, 43.598690145347135], "dst_loc": [-79.3079742573161, 43.79678829137365]}, {"type": "sms", "src_number": "580-3656", "dst_number": "129-8541", "time": "2018-01-20 03:58:17", "src_loc": [-79.32195651460633, 43.59897057086027], "dst_loc": [-79.65952112992085, 43.59589816491015]}, {"type": "sms", "src_number": "611-2902", "dst_number": "730-7684", "time": "2018-01-20 04:32:24", "src_loc": [-79.44811504277381, 43.642131403827925], "dst_loc": [-79.64174824086425, 43.72225532452896]}, {"type": "call", "src_number": "380-5011", "dst_number": "533-1214", "time": "2018-01-20 18:36:49", "duration": 29, "src_loc": [-79.39475192297076, 43.672078165512296], "dst_loc": [-79.39213970696615, 43.60049185466551]}, {"type": "sms", "src_number": "178-3378", "dst_number": "482-2360", "time": "2018-01-20 19:30:32", "src_loc": [-79.50431310479146, 43.60890857891749], "dst_loc": [-79.25501218022124, 43.7201844211002]}, {"type": "sms", "src_number": "286-6787", "dst_number": "698-0146", "time": "2018-01-20 22:03:48", "src_loc": [-79.388594479766, 43.63250266828986], "dst_loc": [-79.48467603872778, 43.73810914176037]}, {"type": "call", "src_number": "961-1759", "dst_number": "638-5474", "time": "2018-01-21 00:08:28", "duration": 222, "src_loc": [-79.42693903873784, 43.7457866145103], "dst_loc": [-79.6940153592882, 43.70432453350932]}, {"type": "sms", "src_number": "346-8644", "dst_number": "496-5543", "time": "2018-01-21 01:10:12", "src_loc": [-79.25934654082104, 43.59115342585325], "dst_loc": [-79.2488629921946, 43.620589347701454]}, {"type": "call", "src_number": "839-6337", "dst_number": "914-9837", "time": "2018-01-21 02:22:31", "duration": 33, "src_loc": [-79.25700024946882, 43.59059501635848], "dst_loc": [-79.39069934571391, 43.64917736033666]}, {"type": "call", "src_number": "603-0289", "dst_number": "698-0146", "time": "2018-01-21 03:40:39", "duration": 281, "src_loc": [-79.46991910175562, 43.693364717337495], "dst_loc": [-79.6871469523973, 43.598983504299134]}, {"type": "call", "src_number": "142-5525", "dst_number": "413-0551", "time": "2018-01-21 04:30:53", "duration": 313, "src_loc": [-79.60308774663169, 43.78719360797569], "dst_loc": [-79.67688937837454, 43.734706647168174]}, {"type": "sms", "src_number": "542-8018", "dst_number": "676-5324", "time": "2018-01-21 04:57:00", "src_loc": [-79.29754906855744, 43.792706635349674], "dst_loc": [-79.34735119504812, 43.5847522707497]}, {"type": "call", "src_number": "698-0146", "dst_number": "926-4806", "time": "2018-01-21 05:23:01", "duration": 96, "src_loc": [-79.50835593859703, 43.57736676538369], "dst_loc": [-79.63071091652361, 43.59869045766793]}, {"type": "sms", "src_number": "048-4695", "dst_number": "533-1214", "time": "2018-01-21 06:20:27", "src_loc": [-79.40676168631632, 43.796155464586526], "dst_loc": [-79.36764636564482, 43.76830707269861]}, {"type": "sms", "src_number": "430-4508", "dst_number": "657-3888", "time": "2018-01-21 06:43:49", "src_loc": [-79.36965642437345, 43.637185058172555], "dst_loc": [-79.52935290861897, 43.786095526813796]}, {"type": "call", "src_number": "137-0722", "dst_number": "560-7837", "time": "2018-01-21 14:57:52", "duration": 266, "src_loc": [-79.65299796177628, 43.59095958484395], "dst_loc": [-79.2603123364348, 43.79310313460459]}, {"type": "call", "src_number": "202-2830", "dst_number": "119-8468", "time": "2018-01-21 15:47:49", "duration": 48, "src_loc": [-79.4335426171567, 43.761029393603984], "dst_loc": [-79.2073948437806, 43.57842811858463]}, {"type": "sms", "src_number": "644-8064", "dst_number": "649-2182", "time": "2018-01-21 17:53:12", "src_loc": [-79.41129050490902, 43.69847657629391], "dst_loc": [-79.66501775785598, 43.62951472924933]}, {"type": "call", "src_number": "638-5474", "dst_number": "067-3729", "time": "2018-01-21 18:46:13", "duration": 258, "src_loc": [-79.69669915258494, 43.7597190374779], "dst_loc": [-79.49609774798303, 43.635729856841884]}, {"type": "call", "src_number": "575-6924", "dst_number": "987-2747", "time": "2018-01-21 21:23:26", "duration": 352, "src_loc": [-79.61348182001623, 43.6340185698654], "dst_loc": [-79.66083492007083, 43.73853867532899]}, {"type": "sms", "src_number": "496-5543", "dst_number": "649-2182", "time": "2018-01-21 23:22:52", "src_loc": [-79.24535597624653, 43.70730940670747], "dst_loc": [-79.47404813864765, 43.629323472775404]}, {"type": "sms", "src_number": "198-0536", "dst_number": "161-4158", "time": "2018-01-22 00:03:32", "src_loc": [-79.38429136626843, 43.60491269338669], "dst_loc": [-79.25981437198686, 43.75649575756238]}, {"type": "call", "src_number": "469-3515", "dst_number": "831-3310", "time": "2018-01-22 00:13:52", "duration": 4, "src_loc": [-79.6969883234294, 43.7296933535067], "dst_loc": [-79.20076881793452, 43.784787868341894]}, {"type": "sms", "src_number": "843-6834", "dst_number": "056-7577", "time": "2018-01-22 00:43:21", "src_loc": [-79.31631182326198, 43.79883302442272], "dst_loc": [-79.36075387996522, 43.61555619488839]}, {"type": "sms", "src_number": "047-5142", "dst_number": "698-0146", "time": "2018-01-22 02:47:47", "src_loc": [-79.31730440584559, 43.6349852596983], "dst_loc": [-79.34580184877977, 43.676622756779636]}, {"type": "sms", "src_number": "013-3456", "dst_number": "695-4637", "time": "2018-01-22 04:20:16", "src_loc": [-79.49383239649424, 43.704467027357715], "dst_loc": [-79.55475051186458, 43.72230508640807]}, {"type": "call", "src_number": "617-3262", "dst_number": "010-3671", "time": "2018-01-22 08:11:53", "duration": 52, "src_loc": [-79.31466365699735, 43.59187900584653], "dst_loc": [-79.28003690544602, 43.59081628862855]}, {"type": "call", "src_number": "731-6163", "dst_number": "048-4695", "time": "2018-01-22 12:49:38", "duration": 121, "src_loc": [-79.62887920949605, 43.79450383516973], "dst_loc": [-79.24884989502628, 43.78603959016892]}, {"type": "sms", "src_number": "839-0038", "dst_number": "560-7837", "time": "2018-01-22 15:25:37", "src_loc": [-79.33604579084809, 43.77835390189126], "dst_loc": [-79.62590349243288, 43.78143806988125]}, {"type": "sms", "src_number": "329-4692", "dst_number": "430-4508", "time": "2018-01-22 18:45:32", "src_loc": [-79.41084631688098, 43.767389840584215], "dst_loc": [-79.49209712261748, 43.66753689588675]}, {"type": "sms", "src_number": "137-0722", "dst_number": "832-2301", "time": "2018-01-22 18:56:55", "src_loc": [-79.20776708704688, 43.77214946693375], "dst_loc": [-79.34903155102431, 43.59471092325802]}, {"type": "call", "src_number": "839-7840", "dst_number": "580-3656", "time": "2018-01-22 21:48:19", "duration": 310, "src_loc": [-79.48825091452466, 43.724351808606805], "dst_loc": [-79.68557556850426, 43.58469287043672]}, {"type": "call", "src_number": "843-6834", "dst_number": "493-8650", "time": "2018-01-23 05:41:53", "duration": 312, "src_loc": [-79.67135544851801, 43.76621775571118], "dst_loc": [-79.26546399606366, 43.65617068258375]}, {"type": "sms", "src_number": "493-8650", "dst_number": "247-2510", "time": "2018-01-23 06:58:38", "src_loc": [-79.47216024351894, 43.58965147332041], "dst_loc": [-79.4998860513344, 43.69417855633002]}, {"type": "sms", "src_number": "029-7245", "dst_number": "161-4158", "time": "2018-01-23 08:27:37", "src_loc": [-79.49820553091122, 43.60623276466478], "dst_loc": [-79.43461865422536, 43.685839978257476]}, {"type": "sms", "src_number": "661-9241", "dst_number": "657-3888", "time": "2018-01-23 10:22:52", "src_loc": [-79.32232629097847, 43.79048525490957], "dst_loc": [-79.3616281230811, 43.62566608999743]}, {"type": "sms", "src_number": "776-2120", "dst_number": "277-0239", "time": "2018-01-23 11:09:15", "src_loc": [-79.30492750710071, 43.76256545010779], "dst_loc": [-79.44696268934048, 43.75542923263458]}, {"type": "call", "src_number": "059-4057", "dst_number": "942-5962", "time": "2018-01-23 12:06:47", "duration": 303, "src_loc": [-79.57456555766791, 43.59618168109082], "dst_loc": [-79.64539276795946, 43.681015804206965]}, {"type": "call", "src_number": "776-2120", "dst_number": "657-3888", "time": "2018-01-23 12:13:55", "duration": 99, "src_loc": [-79.42396404214395, 43.717849035161244], "dst_loc": [-79.21561924314845, 43.7343214482518]}, {"type": "sms", "src_number": "938-6680", "dst_number": "711-1347", "time": "2018-01-23 12:44:28", "src_loc": [-79.23971004603236, 43.7299780724881], "dst_loc": [-79.57153730223331, 43.60459838457176]}, {"type": "call", "src_number": "380-5011", "dst_number": "349-5337", "time": "2018-01-23 13:04:27", "duration": 91, "src_loc": [-79.51733915230444, 43.616608466757604], "dst_loc": [-79.21502820321126, 43.76109710510173]}, {"type": "call", "src_number": "940-6196", "dst_number": "776-2120", "time": "2018-01-23 23:52:52", "duration": 149, "src_loc": [-79.37206789322589, 43.642541987075084], "dst_loc": [-79.35228977266792, 43.77107036138659]}, {"type": "call", "src_number": "390-1713", "dst_number": "285-3740", "time": "2018-01-24 00:01:58", "duration": 53, "src_loc": [-79.44216931954546, 43.729776846073705], "dst_loc": [-79.38400475108219, 43.69523000703148]}, {"type": "sms", "src_number": "929-3363", "dst_number": "412-2420", "time": "2018-01-24 01:40:45", "src_loc": [-79.5755410800629, 43.76844313279314], "dst_loc": [-79.56244842916472, 43.62140423616982]}, {"type": "sms", "src_number": "286-6787", "dst_number": "843-6834", "time": "2018-01-24 04:19:11", "src_loc": [-79.52750762104286, 43.687780635517484], "dst_loc": [-79.6811231328201, 43.75965814481849]}, {"type": "call", "src_number": "942-5962", "dst_number": "129-8541", "time": "2018-01-24 07:05:36", "duration": 312, "src_loc": [-79.46400048486943, 43.74351338922679], "dst_loc": [-79.69584181230452, 43.6505534851455]}, {"type": "sms", "src_number": "784-0925", "dst_number": "961-1759", "time": "2018-01-24 09:38:42", "src_loc": [-79.37519909122376, 43.69014207623327], "dst_loc": [-79.21480617318502, 43.647475833773534]}, {"type": "call", "src_number": "418-9185", "dst_number": "824-8496", "time": "2018-01-24 20:49:18", "duration": 67, "src_loc": [-79.2548745723548, 43.77389072610522], "dst_loc": [-79.3673135089727, 43.6178250686217]}, {"type": "call", "src_number": "493-8650", "dst_number": "956-4003", "time": "2018-01-24 21:13:12", "duration": 200, "src_loc": [-79.44101869263697, 43.66852075252018], "dst_loc": [-79.29091934087043, 43.65623774688806]}, {"type": "call", "src_number": "459-5273", "dst_number": "095-6518", "time": "2018-01-24 21:16:45", "duration": 21, "src_loc": [-79.52577607049896, 43.79106341610474], "dst_loc": [-79.68148393651872, 43.590897048073884]}, {"type": "sms", "src_number": "708-6848", "dst_number": "580-3656", "time": "2018-01-25 09:57:53", "src_loc": [-79.38892898815868, 43.7593732516679], "dst_loc": [-79.20794718828536, 43.77646539328944]}, {"type": "call", "src_number": "059-4057", "dst_number": "776-2120", "time": "2018-01-25 10:59:52", "duration": 329, "src_loc": [-79.24745368790344, 43.740394582087774], "dst_loc": [-79.60364559056528, 43.64003539999735]}, {"type": "call", "src_number": "862-3646", "dst_number": "542-4197", "time": "2018-01-25 11:21:47", "duration": 251, "src_loc": [-79.29629777119764, 43.7268925653678], "dst_loc": [-79.48324834916122, 43.660938107792646]}, {"type": "sms", "src_number": "845-3937", "dst_number": "871-6653", "time": "2018-01-25 14:59:23", "src_loc": [-79.37232531420592, 43.71854080145652], "dst_loc": [-79.22604450610758, 43.75443656446287]}, {"type": "call", "src_number": "247-2510", "dst_number": "674-6199", "time": "2018-01-25 15:38:40", "duration": 118, "src_loc": [-79.35210605516903, 43.70112326222877], "dst_loc": [-79.37776858984323, 43.63604069833302]}, {"type": "sms", "src_number": "932-2436", "dst_number": "806-5506", "time": "2018-01-25 15:43:44", "src_loc": [-79.41907087064591, 43.59962385412002], "dst_loc": [-79.34737779991472, 43.65619984701492]}, {"type": "call", "src_number": "744-6324", "dst_number": "285-3740", "time": "2018-01-25 19:10:42", "duration": 40, "src_loc": [-79.20746148467572, 43.67831946011683], "dst_loc": [-79.39851182075648, 43.73861069570749]}, {"type": "sms", "src_number": "637-0682", "dst_number": "101-7414", "time": "2018-01-26 00:59:47", "src_loc": [-79.52172519136246, 43.77941197368062], "dst_loc": [-79.41158888712019, 43.73390780590735]}, {"type": "sms", "src_number": "202-2830", "dst_number": "444-0066", "time": "2018-01-26 04:54:27", "src_loc": [-79.60518664289192, 43.59213720725489], "dst_loc": [-79.48566700220451, 43.71432456739532]}, {"type": "sms", "src_number": "497-8688", "dst_number": "961-3055", "time": "2018-01-26 08:25:46", "src_loc": [-79.29012448008248, 43.689197100711105], "dst_loc": [-79.29463495049946, 43.74634955451005]}, {"type": "call", "src_number": "575-6924", "dst_number": "520-2727", "time": "2018-01-26 09:45:40", "duration": 78, "src_loc": [-79.63915265056835, 43.63322956282956], "dst_loc": [-79.36012838561187, 43.786348333516095]}, {"type": "call", "src_number": "346-8644", "dst_number": "839-6337", "time": "2018-01-26 10:25:00", "duration": 82, "src_loc": [-79.36154033840273, 43.730186650410175], "dst_loc": [-79.25800477155383, 43.74228087753397]}, {"type": "sms", "src_number": "285-3740", "dst_number": "241-0963", "time": "2018-01-26 18:38:44", "src_loc": [-79.23660140157315, 43.75439753712012], "dst_loc": [-79.21483702512712, 43.6966658081884]}, {"type": "call", "src_number": "629-6198", "dst_number": "913-2332", "time": "2018-01-26 23:03:43", "duration": 182, "src_loc": [-79.68105786894618, 43.68162888027694], "dst_loc": [-79.42180834712057, 43.71529935459478]}, {"type": "sms", "src_number": "824-8496", "dst_number": "592-4790", "time": "2018-01-27 00:15:48", "src_loc": [-79.69386882406415, 43.73124862125219], "dst_loc": [-79.56193826622946, 43.7470386492027]}, {"type": "sms", "src_number": "056-7577", "dst_number": "668-3514", "time": "2018-01-27 00:48:03", "src_loc": [-79.65070798625861, 43.59634170768844], "dst_loc": [-79.51844882126608, 43.760151355034544]}, {"type": "sms", "src_number": "202-2830", "dst_number": "293-2025", "time": "2018-01-27 00:52:30", "src_loc": [-79.44169337736069, 43.72118757117931], "dst_loc": [-79.6212961908827, 43.6480111199901]}, {"type": "call", "src_number": "329-4692", "dst_number": "947-1649", "time": "2018-01-27 02:26:15", "duration": 192, "src_loc": [-79.51286911820904, 43.72659884757036], "dst_loc": [-79.44342968240649, 43.60922856023945]}, {"type": "sms", "src_number": "497-8688", "dst_number": "029-7245", "time": "2018-01-27 19:06:54", "src_loc": [-79.31670862490998, 43.68514662262689], "dst_loc": [-79.23047075501862, 43.58885527890601]}, {"type": "call", "src_number": "839-0275", "dst_number": "649-2182", "time": "2018-01-28 01:19:03", "duration": 231, "src_loc": [-79.58276800519499, 43.602925525741824], "dst_loc": [-79.51843083465954, 43.70174159821493]}, {"type": "call", "src_number": "932-2436", "dst_number": "845-3937", "time": "2018-01-28 05:47:21", "duration": 212, "src_loc": [-79.318048694126, 43.74283094322586], "dst_loc": [-79.23001181052297, 43.64685518920275]}, {"type": "sms", "src_number": "862-3646", "dst_number": "629-5175", "time": "2018-01-28 08:19:54", "src_loc": [-79.28886518998995, 43.79349351017181], "dst_loc": [-79.5268844902694, 43.578521147973504]}, {"type": "call", "src_number": "380-5011", "dst_number": "285-3740", "time": "2018-01-28 09:03:02", "duration": 349, "src_loc": [-79.23136974144079, 43.69574811389531], "dst_loc": [-79.46247850951711, 43.795230994253664]}, {"type": "sms", "src_number": "285-3740", "dst_number": "971-4536", "time": "2018-01-28 11:39:03", "src_loc": [-79.64895145112543, 43.57896881478702], "dst_loc": [-79.5080645035465, 43.58210635436842]}, {"type": "call", "src_number": "390-1713", "dst_number": "695-4637", "time": "2018-01-28 17:19:57", "duration": 139, "src_loc": [-79.4232354067864, 43.6621759626488], "dst_loc": [-79.204746004463, 43.595897974870184]}, {"type": "sms", "src_number": "126-2700", "dst_number": "159-1100", "time": "2018-01-28 19:43:29", "src_loc": [-79.40721281788058, 43.76061275647351], "dst_loc": [-79.30972419537653, 43.63596240019874]}, {"type": "call", "src_number": "628-9627", "dst_number": "390-1713", "time": "2018-01-29 00:49:30", "duration": 5, "src_loc": [-79.56188152138881, 43.7757660338543], "dst_loc": [-79.25143670158559, 43.66100511194425]}, {"type": "call", "src_number": "871-6653", "dst_number": "832-2301", "time": "2018-01-29 03:30:44", "duration": 15, "src_loc": [-79.66105625118242, 43.610038440347466], "dst_loc": [-79.64449803183207, 43.583986914001045]}, {"type": "sms", "src_number": "009-8583", "dst_number": "099-1900", "time": "2018-01-29 05:27:56", "src_loc": [-79.44763796273352, 43.72409451608506], "dst_loc": [-79.21151295435632, 43.78418057220514]}, {"type": "call", "src_number": "285-3740", "dst_number": "731-6514", "time": "2018-01-29 12:00:06", "duration": 113, "src_loc": [-79.42015921471244, 43.742265841684244], "dst_loc": [-79.29915264328564, 43.647434385305544]}, {"type": "sms", "src_number": "067-3729", "dst_number": "676-5324", "time": "2018-01-30 01:45:27", "src_loc": [-79.48040070524188, 43.68476275104457], "dst_loc": [-79.38913423734387, 43.76211995151156]}, {"type": "call", "src_number": "482-2360", "dst_number": "832-2301", "time": "2018-01-30 02:16:44", "duration": 36, "src_loc": [-79.31926656175987, 43.778339151398264], "dst_loc": [-79.4910034862741, 43.68251941920273]}, {"type": "call", "src_number": "328-2096", "dst_number": "681-8883", "time": "2018-01-30 08:16:29", "duration": 43, "src_loc": [-79.63221492483403, 43.5861303851702], "dst_loc": [-79.44091670792623, 43.58397191634043]}, {"type": "sms", "src_number": "198-0536", "dst_number": "459-5273", "time": "2018-01-30 09:55:11", "src_loc": [-79.40489454260849, 43.60657385491975], "dst_loc": [-79.19640356834519, 43.63562651286339]}, {"type": "call", "src_number": "839-0275", "dst_number": "862-3646", "time": "2018-01-30 14:47:48", "duration": 95, "src_loc": [-79.5148500774525, 43.653098347822], "dst_loc": [-79.48599863925298, 43.69974524409387]}, {"type": "call", "src_number": "926-4806", "dst_number": "112-9752", "time": "2018-01-31 02:11:20", "duration": 228, "src_loc": [-79.60082273195718, 43.70537002067801], "dst_loc": [-79.28188185354212, 43.61822195913548]}, {"type": "sms", "src_number": "676-5324", "dst_number": "835-1248", "time": "2018-01-31 05:07:01", "src_loc": [-79.43532949709552, 43.645702431674], "dst_loc": [-79.43958533497397, 43.69420236597903]}, {"type": "call", "src_number": "560-7837", "dst_number": "126-2700", "time": "2018-01-31 09:08:03", "duration": 279, "src_loc": [-79.43815738977798, 43.596576925812265], "dst_loc": [-79.46369037050337, 43.71829966311802]}, {"type": "sms", "src_number": "334-0547", "dst_number": "661-9241", "time": "2018-01-31 11:26:37", "src_loc": [-79.27374406079602, 43.6592528077849], "dst_loc": [-79.59883662809256, 43.605039312853194]}, {"type": "call", "src_number": "126-2700", "dst_number": "676-5324", "time": "2018-01-31 11:56:17", "duration": 65, "src_loc": [-79.47695722644201, 43.74638031371519], "dst_loc": [-79.2928180653875, 43.711575570903065]}, {"type": "call", "src_number": "661-9241", "dst_number": "338-7780", "time": "2018-01-31 17:47:05", "duration": 94, "src_loc": [-79.42089064034344, 43.73671676345132], "dst_loc": [-79.53025381998772, 43.63582016584701]}, {"type": "call", "src_number": "126-2700", "dst_number": "346-8644", "time": "2018-01-31 18:35:15", "duration": 36, "src_loc": [-79.20728076581803, 43.68284307854063], "dst_loc": [-79.21236152179357, 43.77856150434104]}, {"type": "call", "src_number": "430-4508", "dst_number": "183-7942", "time": "2018-02-01 01:14:50", "duration": 206, "src_loc": [-79.44392973954831, 43.57913761585067], "dst_loc": [-79.65297110911233, 43.69636331988847]}, {"type": "call", "src_number": "129-8541", "dst_number": "059-4057", "time": "2018-02-01 03:47:12", "duration": 273, "src_loc": [-79.31294107443713, 43.770462079095225], "dst_loc": [-79.20641366417783, 43.727039117929564]}, {"type": "call", "src_number": "698-0146", "dst_number": "401-3187", "time": "2018-02-01 03:59:48", "duration": 188, "src_loc": [-79.53479752237013, 43.698192783077005], "dst_loc": [-79.32956005752676, 43.74415343922767]}, {"type": "call", "src_number": "926-4806", "dst_number": "159-1100", "time": "2018-02-01 08:54:01", "duration": 120, "src_loc": [-79.29602436929974, 43.76481069900668], "dst_loc": [-79.49823148701165, 43.77943498486238]}, {"type": "call", "src_number": "241-0963", "dst_number": "475-6203", "time": "2018-02-01 09:09:49", "duration": 159, "src_loc": [-79.63069880729697, 43.72739718512879], "dst_loc": [-79.58518244924932, 43.74383934359888]}, {"type": "call", "src_number": "009-8221", "dst_number": "831-3310", "time": "2018-02-01 14:16:17", "duration": 235, "src_loc": [-79.56263648608967, 43.66186172510967], "dst_loc": [-79.68131638427552, 43.76105366548707]}, {"type": "call", "src_number": "137-0722", "dst_number": "520-2727", "time": "2018-02-01 23:32:59", "duration": 80, "src_loc": [-79.27549498397805, 43.60261316055172], "dst_loc": [-79.36831953425661, 43.76753780441939]}, {"type": "call", "src_number": "418-9185", "dst_number": "993-3441", "time": "2018-02-02 02:23:50", "duration": 250, "src_loc": [-79.36550637781389, 43.63299305250695], "dst_loc": [-79.6815252114666, 43.74277135632668]}, {"type": "sms", "src_number": "059-4057", "dst_number": "661-9241", "time": "2018-02-02 10:31:54", "src_loc": [-79.48526776481562, 43.63948319613935], "dst_loc": [-79.58137362542784, 43.6460733538646]}, {"type": "sms", "src_number": "178-3378", "dst_number": "161-4158", "time": "2018-02-02 11:01:08", "src_loc": [-79.29049741567236, 43.69108334949572], "dst_loc": [-79.49483516097797, 43.726862538951295]}, {"type": "sms", "src_number": "439-9218", "dst_number": "373-3350", "time": "2018-02-02 11:36:10", "src_loc": [-79.61297481364608, 43.69270330071979], "dst_loc": [-79.66978159665403, 43.71393321434367]}, {"type": "sms", "src_number": "112-9752", "dst_number": "178-3378", "time": "2018-02-02 11:55:01", "src_loc": [-79.6403493192673, 43.79818858943891], "dst_loc": [-79.21339996584558, 43.610815391971244]}, {"type": "call", "src_number": "300-7295", "dst_number": "839-7840", "time": "2018-02-02 12:03:47", "duration": 114, "src_loc": [-79.42896610687262, 43.76635038565814], "dst_loc": [-79.46880980920375, 43.67294752286088]}, {"type": "sms", "src_number": "119-8468", "dst_number": "676-5324", "time": "2018-02-02 17:24:56", "src_loc": [-79.46855620651709, 43.750473492307805], "dst_loc": [-79.2800066737995, 43.665221689214846]}, {"type": "call", "src_number": "654-6297", "dst_number": "469-3515", "time": "2018-02-02 18:10:46", "duration": 180, "src_loc": [-79.5420562448553, 43.63646440262321], "dst_loc": [-79.38119880306236, 43.74449302190998]}, {"type": "sms", "src_number": "059-4057", "dst_number": "914-9837", "time": "2018-02-02 18:21:34", "src_loc": [-79.49262228540844, 43.79003248476327], "dst_loc": [-79.6349305284666, 43.74657232394516]}, {"type": "call", "src_number": "649-2182", "dst_number": "328-2096", "time": "2018-02-02 18:24:47", "duration": 153, "src_loc": [-79.59701899393654, 43.79530107283097], "dst_loc": [-79.1990666971706, 43.663011301027936]}, {"type": "call", "src_number": "722-8592", "dst_number": "938-6680", "time": "2018-02-02 21:13:20", "duration": 303, "src_loc": [-79.52012925837687, 43.729827279139336], "dst_loc": [-79.67051683847932, 43.70555104064998]}, {"type": "call", "src_number": "580-3656", "dst_number": "936-4231", "time": "2018-02-03 01:02:10", "duration": 35, "src_loc": [-79.59379549441951, 43.58613830562145], "dst_loc": [-79.34309252413294, 43.67807200257415]}, {"type": "call", "src_number": "731-6514", "dst_number": "654-6297", "time": "2018-02-03 01:48:12", "duration": 51, "src_loc": [-79.24152152060935, 43.77335817052213], "dst_loc": [-79.63393739487049, 43.674258586249614]}, {"type": "sms", "src_number": "180-2368", "dst_number": "629-6198", "time": "2018-02-03 03:58:14", "src_loc": [-79.40529440282693, 43.65534494933468], "dst_loc": [-79.68981104265262, 43.654247753461505]}, {"type": "sms", "src_number": "993-3441", "dst_number": "537-6361", "time": "2018-02-03 04:59:38", "src_loc": [-79.26476159865146, 43.67770205416232], "dst_loc": [-79.4883690435545, 43.7788497281896]}, {"type": "call", "src_number": "390-1713", "dst_number": "681-8883", "time": "2018-02-03 07:50:31", "duration": 288, "src_loc": [-79.53197513750808, 43.66163057836688], "dst_loc": [-79.29992855208141, 43.5864775979829]}, {"type": "sms", "src_number": "165-7838", "dst_number": "839-6337", "time": "2018-02-03 08:34:56", "src_loc": [-79.61962621532705, 43.67618593796325], "dst_loc": [-79.26348873835799, 43.76211644400444]}, {"type": "call", "src_number": "439-9218", "dst_number": "993-4723", "time": "2018-02-03 09:35:48", "duration": 275, "src_loc": [-79.51980993158973, 43.614679537802395], "dst_loc": [-79.28710301324402, 43.64250797350385]}, {"type": "sms", "src_number": "831-3310", "dst_number": "247-2510", "time": "2018-02-03 10:13:16", "src_loc": [-79.23947341907136, 43.73770328668601], "dst_loc": [-79.21419036955903, 43.64796866377672]}, {"type": "call", "src_number": "137-5770", "dst_number": "033-9988", "time": "2018-02-04 03:18:38", "duration": 281, "src_loc": [-79.30647843260255, 43.61849789426932], "dst_loc": [-79.62607490934717, 43.663433403991185]}, {"type": "sms", "src_number": "286-6787", "dst_number": "839-0275", "time": "2018-02-04 04:30:46", "src_loc": [-79.28924196383198, 43.60669049972329], "dst_loc": [-79.69109249147691, 43.603530123072915]}, {"type": "sms", "src_number": "614-5364", "dst_number": "839-0275", "time": "2018-02-04 06:55:14", "src_loc": [-79.54506673736475, 43.660675946685586], "dst_loc": [-79.4812498074273, 43.60855281322197]}, {"type": "sms", "src_number": "575-6924", "dst_number": "614-5364", "time": "2018-02-04 07:34:54", "src_loc": [-79.38071737982096, 43.6012020073388], "dst_loc": [-79.37704696676359, 43.75588513215892]}, {"type": "call", "src_number": "649-0500", "dst_number": "649-2182", "time": "2018-02-04 08:30:34", "duration": 293, "src_loc": [-79.56805617289272, 43.71831231702281], "dst_loc": [-79.32987589685374, 43.64933479390777]}, {"type": "sms", "src_number": "475-6203", "dst_number": "914-9837", "time": "2018-02-04 10:43:27", "src_loc": [-79.19922594004407, 43.73207147210225], "dst_loc": [-79.58763135230544, 43.669521446730826]}, {"type": "sms", "src_number": "059-4057", "dst_number": "731-6163", "time": "2018-02-04 10:48:32", "src_loc": [-79.5419472417652, 43.630488940704225], "dst_loc": [-79.42272665485181, 43.66569410889687]}, {"type": "sms", "src_number": "708-6848", "dst_number": "129-8541", "time": "2018-02-05 00:04:20", "src_loc": [-79.41329787303927, 43.5778491604278], "dst_loc": [-79.4817812971661, 43.68990081015766]}, {"type": "sms", "src_number": "493-8650", "dst_number": "102-9506", "time": "2018-02-05 02:35:49", "src_loc": [-79.32885769401491, 43.79029454474403], "dst_loc": [-79.34726680983579, 43.6373875463582]}, {"type": "sms", "src_number": "776-2120", "dst_number": "784-0925", "time": "2018-02-05 02:54:54", "src_loc": [-79.4005474049553, 43.704529280436155], "dst_loc": [-79.21874893664629, 43.65915058647817]}, {"type": "call", "src_number": "119-8468", "dst_number": "657-3888", "time": "2018-02-05 03:50:45", "duration": 99, "src_loc": [-79.19872279378284, 43.68705768634837], "dst_loc": [-79.53889046496305, 43.68117706401541]}, {"type": "sms", "src_number": "676-5324", "dst_number": "165-7838", "time": "2018-02-05 05:16:44", "src_loc": [-79.48785771202196, 43.74040911938776], "dst_loc": [-79.47903311651437, 43.70342428109974]}, {"type": "call", "src_number": "731-6163", "dst_number": "661-9241", "time": "2018-02-05 07:22:31", "duration": 181, "src_loc": [-79.31990458827934, 43.609135539851906], "dst_loc": [-79.42811344654699, 43.6942363341473]}, {"type": "call", "src_number": "119-8468", "dst_number": "956-4003", "time": "2018-02-05 08:17:03", "duration": 267, "src_loc": [-79.47471480703965, 43.605341837492006], "dst_loc": [-79.48994207209421, 43.59894460542449]}, {"type": "sms", "src_number": "820-8986", "dst_number": "277-0239", "time": "2018-02-05 14:27:39", "src_loc": [-79.26405877236944, 43.768000500044344], "dst_loc": [-79.53955902106232, 43.58529685015662]}, {"type": "call", "src_number": "674-6199", "dst_number": "100-8771", "time": "2018-02-05 18:49:35", "duration": 336, "src_loc": [-79.21558491106569, 43.641638169040704], "dst_loc": [-79.30400146423794, 43.605296042616374]}, {"type": "sms", "src_number": "137-5770", "dst_number": "183-7942", "time": "2018-02-05 19:52:25", "src_loc": [-79.61269592475422, 43.76723452470577], "dst_loc": [-79.36183835613542, 43.58996859385132]}, {"type": "sms", "src_number": "835-1248", "dst_number": "776-2120", "time": "2018-02-05 22:39:32", "src_loc": [-79.69705410430369, 43.72194950150018], "dst_loc": [-79.45295942170094, 43.7761273436893]}, {"type": "sms", "src_number": "607-9319", "dst_number": "463-0109", "time": "2018-02-06 01:38:11", "src_loc": [-79.35435955738576, 43.77285160563999], "dst_loc": [-79.46025665894794, 43.7627883963207]}, {"type": "call", "src_number": "801-9566", "dst_number": "475-6203", "time": "2018-02-06 03:29:19", "duration": 316, "src_loc": [-79.43394215289825, 43.721585643954775], "dst_loc": [-79.54817575140875, 43.58421620096383]}, {"type": "call", "src_number": "300-7295", "dst_number": "183-7942", "time": "2018-02-06 04:32:05", "duration": 58, "src_loc": [-79.3872903552524, 43.79109175346116], "dst_loc": [-79.62178983641677, 43.66551997613182]}, {"type": "sms", "src_number": "629-6198", "dst_number": "674-6199", "time": "2018-02-06 04:55:38", "src_loc": [-79.48254176706176, 43.63234722804406], "dst_loc": [-79.20509630342629, 43.789121214827986]}, {"type": "sms", "src_number": "843-6834", "dst_number": "560-7837", "time": "2018-02-06 10:13:34", "src_loc": [-79.25663004461899, 43.6452102511964], "dst_loc": [-79.52079809222538, 43.702719881430255]}, {"type": "call", "src_number": "346-8644", "dst_number": "832-2301", "time": "2018-02-06 16:24:11", "duration": 209, "src_loc": [-79.38595355229852, 43.69035590686048], "dst_loc": [-79.53842908647405, 43.657889855011994]}, {"type": "sms", "src_number": "198-0536", "dst_number": "731-6163", "time": "2018-02-07 00:52:49", "src_loc": [-79.50424001644481, 43.72732052102847], "dst_loc": [-79.45960379595459, 43.77349325668482]}, {"type": "sms", "src_number": "971-4536", "dst_number": "095-6518", "time": "2018-02-07 01:01:50", "src_loc": [-79.19695759776047, 43.6392601883764], "dst_loc": [-79.31450329252523, 43.70437197898618]}, {"type": "call", "src_number": "715-7430", "dst_number": "731-6163", "time": "2018-02-07 05:52:48", "duration": 338, "src_loc": [-79.22491525189224, 43.652177175018245], "dst_loc": [-79.46731390997299, 43.58828907916325]}, {"type": "call", "src_number": "668-3514", "dst_number": "277-0239", "time": "2018-02-07 15:11:16", "duration": 278, "src_loc": [-79.54751848600986, 43.77470361779713], "dst_loc": [-79.2214412083136, 43.70681519592702]}, {"type": "sms", "src_number": "334-4295", "dst_number": "095-6518", "time": "2018-02-07 18:55:12", "src_loc": [-79.65133659155721, 43.67019134599335], "dst_loc": [-79.2290765553155, 43.76952515730628]}, {"type": "call", "src_number": "839-0038", "dst_number": "617-3262", "time": "2018-02-07 20:09:58", "duration": 94, "src_loc": [-79.5277981640261, 43.6559564846429], "dst_loc": [-79.67207271585505, 43.609061025551426]}, {"type": "call", "src_number": "202-2830", "dst_number": "009-8221", "time": "2018-02-07 21:18:48", "duration": 74, "src_loc": [-79.64293440181073, 43.76437938575971], "dst_loc": [-79.3775855302057, 43.624117443096885]}, {"type": "call", "src_number": "824-8496", "dst_number": "843-6834", "time": "2018-02-07 21:28:29", "duration": 282, "src_loc": [-79.44000778759128, 43.61736005541944], "dst_loc": [-79.31796266055343, 43.79106289160258]}, {"type": "call", "src_number": "711-1347", "dst_number": "971-4536", "time": "2018-02-07 21:53:29", "duration": 28, "src_loc": [-79.49417965578935, 43.67519802210826], "dst_loc": [-79.32524003540486, 43.72523332046841]}, {"type": "sms", "src_number": "180-2368", "dst_number": "839-7840", "time": "2018-02-07 23:08:25", "src_loc": [-79.24795525136203, 43.605176347085894], "dst_loc": [-79.27573514999875, 43.669205447528974]}, {"type": "sms", "src_number": "401-3187", "dst_number": "119-8468", "time": "2018-02-07 23:27:19", "src_loc": [-79.31608742251538, 43.67129540102219], "dst_loc": [-79.26585544199592, 43.775875968576514]}, {"type": "sms", "src_number": "137-0722", "dst_number": "029-7245", "time": "2018-02-07 23:35:59", "src_loc": [-79.64319033734944, 43.635900293822516], "dst_loc": [-79.25649664064656, 43.694384324332134]}, {"type": "call", "src_number": "119-8468", "dst_number": "329-4692", "time": "2018-02-08 03:37:30", "duration": 14, "src_loc": [-79.4450240863912, 43.615143536358694], "dst_loc": [-79.62058645769753, 43.63400587879913]}, {"type": "call", "src_number": "839-0038", "dst_number": "722-8592", "time": "2018-02-08 06:05:30", "duration": 151, "src_loc": [-79.59249755450529, 43.763756747912986], "dst_loc": [-79.59121117358758, 43.78052486620574]}, {"type": "sms", "src_number": "542-4197", "dst_number": "056-7577", "time": "2018-02-08 07:00:13", "src_loc": [-79.59047111317099, 43.77897414682188], "dst_loc": [-79.43882692899136, 43.67869192251501]}, {"type": "call", "src_number": "832-2301", "dst_number": "180-2368", "time": "2018-02-08 08:45:40", "duration": 1, "src_loc": [-79.48458771213588, 43.60734949315318], "dst_loc": [-79.37285908877861, 43.66028123543498]}, {"type": "call", "src_number": "095-6518", "dst_number": "469-3515", "time": "2018-02-08 12:37:18", "duration": 102, "src_loc": [-79.5041356538536, 43.70151389216995], "dst_loc": [-79.30075236481127, 43.739914954214754]}, {"type": "sms", "src_number": "373-3350", "dst_number": "142-5525", "time": "2018-02-08 12:40:49", "src_loc": [-79.68567837223472, 43.799100373341695], "dst_loc": [-79.65460763219274, 43.647171771698325]}, {"type": "call", "src_number": "247-2510", "dst_number": "629-5175", "time": "2018-02-08 13:31:58", "duration": 268, "src_loc": [-79.36071074016057, 43.62623314792673], "dst_loc": [-79.53428119890053, 43.58426688443261]}, {"type": "sms", "src_number": "129-8541", "dst_number": "101-7414", "time": "2018-02-08 17:38:02", "src_loc": [-79.5374522025347, 43.63276944373226], "dst_loc": [-79.68951890753074, 43.58192662574493]}, {"type": "sms", "src_number": "784-0925", "dst_number": "247-2510", "time": "2018-02-08 18:22:11", "src_loc": [-79.48218194526196, 43.69738355796221], "dst_loc": [-79.5123567325659, 43.75950115427946]}, {"type": "call", "src_number": "730-5600", "dst_number": "095-6518", "time": "2018-02-09 00:45:13", "duration": 33, "src_loc": [-79.49718244455997, 43.68919467436366], "dst_loc": [-79.33725647479145, 43.73351761372302]}, {"type": "call", "src_number": "744-6324", "dst_number": "013-3456", "time": "2018-02-09 03:10:03", "duration": 102, "src_loc": [-79.55126894300749, 43.79290702300345], "dst_loc": [-79.52123395172768, 43.71302121852292]}, {"type": "sms", "src_number": "938-6680", "dst_number": "100-8771", "time": "2018-02-09 05:14:37", "src_loc": [-79.20791069928809, 43.748437463922244], "dst_loc": [-79.58902493003163, 43.64935712237218]}, {"type": "call", "src_number": "100-8771", "dst_number": "067-3729", "time": "2018-02-09 05:25:10", "duration": 51, "src_loc": [-79.41464659420858, 43.628869185398074], "dst_loc": [-79.48747697407707, 43.66112994680709]}, {"type": "sms", "src_number": "390-1713", "dst_number": "444-0066", "time": "2018-02-09 07:42:08", "src_loc": [-79.34973959681996, 43.72068841815903], "dst_loc": [-79.61490810564082, 43.69386471441907]}, {"type": "sms", "src_number": "849-3472", "dst_number": "006-3878", "time": "2018-02-09 09:57:55", "src_loc": [-79.2760897035372, 43.58352853758888], "dst_loc": [-79.64397786991002, 43.6346018575061]}, {"type": "sms", "src_number": "159-1100", "dst_number": "731-6163", "time": "2018-02-09 18:04:11", "src_loc": [-79.29327314615163, 43.656363360122704], "dst_loc": [-79.56162556707608, 43.71273017212304]}, {"type": "call", "src_number": "731-6163", "dst_number": "942-5962", "time": "2018-02-09 20:26:33", "duration": 347, "src_loc": [-79.4865723985096, 43.63406640145802], "dst_loc": [-79.65494245828853, 43.60544469799307]}, {"type": "call", "src_number": "576-9648", "dst_number": "603-0289", "time": "2018-02-09 23:23:45", "duration": 204, "src_loc": [-79.57199713083054, 43.7455958786449], "dst_loc": [-79.4392800948074, 43.717155440486366]}, {"type": "sms", "src_number": "010-3671", "dst_number": "839-0275", "time": "2018-02-10 01:34:35", "src_loc": [-79.35446719542102, 43.79861474672492], "dst_loc": [-79.33787116763764, 43.668903106517924]}, {"type": "sms", "src_number": "286-6787", "dst_number": "013-3456", "time": "2018-02-10 01:35:31", "src_loc": [-79.62604974172187, 43.693662974186175], "dst_loc": [-79.44413779859205, 43.76810481906296]}, {"type": "sms", "src_number": "744-6324", "dst_number": "067-3729", "time": "2018-02-10 05:11:09", "src_loc": [-79.56641364516517, 43.696612123645124], "dst_loc": [-79.63779729094445, 43.755108735604196]}, {"type": "sms", "src_number": "430-4508", "dst_number": "560-7837", "time": "2018-02-10 05:43:09", "src_loc": [-79.28490853261637, 43.79813409287593], "dst_loc": [-79.51112703335998, 43.71653741116643]}, {"type": "call", "src_number": "099-1900", "dst_number": "101-7414", "time": "2018-02-10 06:48:33", "duration": 269, "src_loc": [-79.31426217305027, 43.746777305148214], "dst_loc": [-79.38373160239351, 43.636734572021275]}, {"type": "sms", "src_number": "178-3378", "dst_number": "469-3515", "time": "2018-02-10 08:23:30", "src_loc": [-79.62866972454897, 43.630010994641694], "dst_loc": [-79.6530129303813, 43.67118923023277]}, {"type": "call", "src_number": "744-6324", "dst_number": "349-5337", "time": "2018-02-10 09:28:14", "duration": 79, "src_loc": [-79.60407893894536, 43.79109927642276], "dst_loc": [-79.42060161800215, 43.60376077486623]}, {"type": "sms", "src_number": "839-7840", "dst_number": "940-6196", "time": "2018-02-10 14:19:59", "src_loc": [-79.64669192776833, 43.7017003287195], "dst_loc": [-79.69204749837195, 43.79565342584212]}, {"type": "call", "src_number": "839-0275", "dst_number": "493-8650", "time": "2018-02-10 14:48:08", "duration": 248, "src_loc": [-79.6507925070743, 43.71761893977619], "dst_loc": [-79.48762564124692, 43.61917976504349]}, {"type": "sms", "src_number": "845-3937", "dst_number": "956-4003", "time": "2018-02-10 16:42:39", "src_loc": [-79.63270489307085, 43.639343699694564], "dst_loc": [-79.60365288687586, 43.76441303525918]}, {"type": "call", "src_number": "095-6518", "dst_number": "496-5543", "time": "2018-02-10 20:49:09", "duration": 159, "src_loc": [-79.60938991021794, 43.597187777240315], "dst_loc": [-79.59369866302585, 43.622894624241766]}, {"type": "call", "src_number": "731-6514", "dst_number": "010-3671", "time": "2018-02-11 04:52:55", "duration": 122, "src_loc": [-79.38607764029744, 43.72272869360939], "dst_loc": [-79.48512645319109, 43.72595738976939]}, {"type": "sms", "src_number": "293-2025", "dst_number": "665-2075", "time": "2018-02-11 05:52:00", "src_loc": [-79.35467407395267, 43.78484212880292], "dst_loc": [-79.5765797866269, 43.76891016383471]}, {"type": "sms", "src_number": "711-1347", "dst_number": "637-0682", "time": "2018-02-11 15:26:18", "src_loc": [-79.6692167063516, 43.58751622823409], "dst_loc": [-79.40028623847388, 43.63337478430788]}, {"type": "call", "src_number": "696-2653", "dst_number": "607-9319", "time": "2018-02-11 23:51:31", "duration": 28, "src_loc": [-79.6260007890523, 43.70511287152723], "dst_loc": [-79.29846050834718, 43.680233117718814]}, {"type": "sms", "src_number": "067-3729", "dst_number": "676-5324", "time": "2018-02-12 02:02:12", "src_loc": [-79.69508888803169, 43.76791880666186], "dst_loc": [-79.40911536012534, 43.685375650174215]}, {"type": "call", "src_number": "493-8650", "dst_number": "300-7295", "time": "2018-02-12...
SOLUTION.PDF

Answer To This Question Is Available To Download

Related Questions & Answers

More Questions »

Submit New Assignment

Copy and Paste Your Assignment Here